Posted to tcl by hypnotoad at Sun Nov 19 16:56:38 GMT 2017view raw

  1. /*
  2. * tclZipfs.c --
  3. *
  4. * Implementation of the ZIP filesystem used in TIP 430
  5. * Adapted from the implentation for AndroWish.
  6. *
  7. * Coptright (c) 2016-2017 Sean Woods <yoda@etoyoc.com>
  8. * Copyright (c) 2013-2015 Christian Werner <chw@ch-werner.de>
  9. *
  10. * See the file "license.terms" for information on usage and redistribution of
  11. * this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12. */
  13.  
  14. #include "tclInt.h"
  15. #include "tclFileSystem.h"
  16.  
  17. #if !defined(_WIN32) && !defined(_WIN64)
  18. #include <sys/mman.h>
  19. #endif
  20. #include <errno.h>
  21. #include <string.h>
  22. #include <sys/stat.h>
  23. #include <time.h>
  24. #include <stdlib.h>
  25. #include <fcntl.h>
  26.  
  27. #ifdef HAVE_ZLIB
  28. #include "zlib.h"
  29. #include "crypt.h"
  30.  
  31. /*
  32. ** On windows we need VFS to look like a volume
  33. ** On Unix we need it to look like a UNC path
  34. */
  35. #define ZIPFS_VOLUME "//zipfs:/"
  36. #define ZIPFS_VOLUME_LEN 9
  37. #define ZIPFS_APP_MOUNT "//zipfs:/app"
  38. #define ZIPFS_ZIP_MOUNT "//zipfs:/lib/tcl"
  39. /*
  40. * Various constants and offsets found in ZIP archive files
  41. */
  42.  
  43. #define ZIP_SIG_LEN 4
  44.  
  45. /* Local header of ZIP archive member (at very beginning of each member). */
  46. #define ZIP_LOCAL_HEADER_SIG 0x04034b50
  47. #define ZIP_LOCAL_HEADER_LEN 30
  48. #define ZIP_LOCAL_SIG_OFFS 0
  49. #define ZIP_LOCAL_VERSION_OFFS 4
  50. #define ZIP_LOCAL_FLAGS_OFFS 6
  51. #define ZIP_LOCAL_COMPMETH_OFFS 8
  52. #define ZIP_LOCAL_MTIME_OFFS 10
  53. #define ZIP_LOCAL_MDATE_OFFS 12
  54. #define ZIP_LOCAL_CRC32_OFFS 14
  55. #define ZIP_LOCAL_COMPLEN_OFFS 18
  56. #define ZIP_LOCAL_UNCOMPLEN_OFFS 22
  57. #define ZIP_LOCAL_PATHLEN_OFFS 26
  58. #define ZIP_LOCAL_EXTRALEN_OFFS 28
  59.  
  60. /* Central header of ZIP archive member at end of ZIP file. */
  61. #define ZIP_CENTRAL_HEADER_SIG 0x02014b50
  62. #define ZIP_CENTRAL_HEADER_LEN 46
  63. #define ZIP_CENTRAL_SIG_OFFS 0
  64. #define ZIP_CENTRAL_VERSIONMADE_OFFS 4
  65. #define ZIP_CENTRAL_VERSION_OFFS 6
  66. #define ZIP_CENTRAL_FLAGS_OFFS 8
  67. #define ZIP_CENTRAL_COMPMETH_OFFS 10
  68. #define ZIP_CENTRAL_MTIME_OFFS 12
  69. #define ZIP_CENTRAL_MDATE_OFFS 14
  70. #define ZIP_CENTRAL_CRC32_OFFS 16
  71. #define ZIP_CENTRAL_COMPLEN_OFFS 20
  72. #define ZIP_CENTRAL_UNCOMPLEN_OFFS 24
  73. #define ZIP_CENTRAL_PATHLEN_OFFS 28
  74. #define ZIP_CENTRAL_EXTRALEN_OFFS 30
  75. #define ZIP_CENTRAL_FCOMMENTLEN_OFFS 32
  76. #define ZIP_CENTRAL_DISKFILE_OFFS 34
  77. #define ZIP_CENTRAL_IATTR_OFFS 36
  78. #define ZIP_CENTRAL_EATTR_OFFS 38
  79. #define ZIP_CENTRAL_LOCALHDR_OFFS 42
  80.  
  81. /* Central end signature at very end of ZIP file. */
  82. #define ZIP_CENTRAL_END_SIG 0x06054b50
  83. #define ZIP_CENTRAL_END_LEN 22
  84. #define ZIP_CENTRAL_END_SIG_OFFS 0
  85. #define ZIP_CENTRAL_DISKNO_OFFS 4
  86. #define ZIP_CENTRAL_DISKDIR_OFFS 6
  87. #define ZIP_CENTRAL_ENTS_OFFS 8
  88. #define ZIP_CENTRAL_TOTALENTS_OFFS 10
  89. #define ZIP_CENTRAL_DIRSIZE_OFFS 12
  90. #define ZIP_CENTRAL_DIRSTART_OFFS 16
  91. #define ZIP_CENTRAL_COMMENTLEN_OFFS 20
  92.  
  93. #define ZIP_MIN_VERSION 20
  94. #define ZIP_COMPMETH_STORED 0
  95. #define ZIP_COMPMETH_DEFLATED 8
  96.  
  97. #define ZIP_PASSWORD_END_SIG 0x5a5a4b50
  98.  
  99. /*
  100. * Macros to read and write 16 and 32 bit integers from/to ZIP archives.
  101. */
  102.  
  103. #define zip_read_int(p) \
  104. ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24))
  105. #define zip_read_short(p) \
  106. ((p)[0] | ((p)[1] << 8))
  107.  
  108. #define zip_write_int(p, v) \
  109. (p)[0] = (v) & 0xff; (p)[1] = ((v) >> 8) & 0xff; \
  110. (p)[2] = ((v) >> 16) & 0xff; (p)[3] = ((v) >> 24) & 0xff;
  111. #define zip_write_short(p, v) \
  112. (p)[0] = (v) & 0xff; (p)[1] = ((v) >> 8) & 0xff;
  113.  
  114. /*
  115. * Windows drive letters.
  116. */
  117.  
  118. #if defined(_WIN32) || defined(_WIN64)
  119. #define HAS_DRIVES 1
  120. static const char drvletters[] =
  121. "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  122. #else
  123. #define HAS_DRIVES 0
  124. #endif
  125.  
  126. /*
  127. * Mutex to protect localtime(3) when no reentrant version available.
  128. */
  129.  
  130. #if !defined(_WIN32) && !defined(_WIN64)
  131. #ifndef HAVE_LOCALTIME_R
  132. #ifdef TCL_THREADS
  133. TCL_DECLARE_MUTEX(localtimeMutex)
  134. #endif
  135. #endif
  136. #endif
  137.  
  138. /*
  139. * In-core description of mounted ZIP archive file.
  140. */
  141.  
  142. typedef struct ZipFile {
  143. char *name; /* Archive name */
  144. Tcl_Channel chan; /* Channel handle or NULL */
  145. unsigned char *data; /* Memory mapped or malloc'ed file */
  146. long length; /* Length of memory mapped file */
  147. unsigned char *tofree; /* Non-NULL if malloc'ed file */
  148. int nfiles; /* Number of files in archive */
  149. int baseoffs; /* Archive start */
  150. int baseoffsp; /* Password start */
  151. int centoffs; /* Archive directory start */
  152. char pwbuf[264]; /* Password buffer */
  153. #if defined(_WIN32) || defined(_WIN64)
  154. HANDLE mh;
  155. #endif
  156. int nopen; /* Number of open files on archive */
  157. struct ZipEntry *entries; /* List of files in archive */
  158. struct ZipEntry *topents; /* List of top-level dirs in archive */
  159. #if HAS_DRIVES
  160. int mntdrv; /* Drive letter of mount point */
  161. #endif
  162. int mntptlen; /* Length of mount point */
  163. char mntpt[1]; /* Mount point */
  164. } ZipFile;
  165.  
  166. /*
  167. * In-core description of file contained in mounted ZIP archive.
  168. */
  169.  
  170. typedef struct ZipEntry {
  171. char *name; /* The full pathname of the virtual file */
  172. ZipFile *zipfile; /* The ZIP file holding this virtual file */
  173. long offset; /* Data offset into memory mapped ZIP file */
  174. int nbyte; /* Uncompressed size of the virtual file */
  175. int nbytecompr; /* Compressed size of the virtual file */
  176. int cmeth; /* Compress method */
  177. int isdir; /* Set to 1 if directory */
  178. int depth; /* Number of slashes in path. */
  179. int crc32; /* CRC-32 */
  180. int timestamp; /* Modification time */
  181. int isenc; /* True if data is encrypted */
  182. unsigned char *data; /* File data if written */
  183. struct ZipEntry *next; /* Next file in the same archive */
  184. struct ZipEntry *tnext; /* Next top-level dir in archive */
  185. } ZipEntry;
  186.  
  187. /*
  188. * File channel for file contained in mounted ZIP archive.
  189. */
  190.  
  191. typedef struct ZipChannel {
  192. ZipFile *zipfile; /* The ZIP file holding this channel */
  193. ZipEntry *zipentry; /* Pointer back to virtual file */
  194. unsigned long nmax; /* Max. size for write */
  195. unsigned long nbyte; /* Number of bytes of uncompressed data */
  196. unsigned long nread; /* Pos of next byte to be read from the channel */
  197. unsigned char *ubuf; /* Pointer to the uncompressed data */
  198. int iscompr; /* True if data is compressed */
  199. int isdir; /* Set to 1 if directory */
  200. int isenc; /* True if data is encrypted */
  201. int iswr; /* True if open for writing */
  202. unsigned long keys[3]; /* Key for decryption */
  203. } ZipChannel;
  204.  
  205. /*
  206. * Global variables.
  207. *
  208. * Most are kept in single ZipFS struct. When build with threading
  209. * support this struct is protected by the ZipFSMutex (see below).
  210. *
  211. * The "fileHash" component is the process wide global table of all known
  212. * ZIP archive members in all mounted ZIP archives.
  213. *
  214. * The "zipHash" components is the process wide global table of all mounted
  215. * ZIP archive files.
  216. */
  217.  
  218. static struct {
  219. int initialized; /* True when initialized */
  220. int lock; /* RW lock, see below */
  221. int waiters; /* RW lock, see below */
  222. int wrmax; /* Maximum write size of a file */
  223. int idCount; /* Counter for channel names */
  224. Tcl_HashTable fileHash; /* File name to ZipEntry mapping */
  225. Tcl_HashTable zipHash; /* Mount to ZipFile mapping */
  226. } ZipFS = {
  227. 0, 0, 0, 0, 0,
  228. };
  229.  
  230. /*
  231. * For password rotation.
  232. */
  233.  
  234. static const char pwrot[16] = {
  235. 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
  236. 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0
  237. };
  238.  
  239. /*
  240. * Table to compute CRC32.
  241. */
  242.  
  243. static const unsigned long crc32tab[256] = {
  244. 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
  245. 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
  246. 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
  247. 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
  248. 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
  249. 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
  250. 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
  251. 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
  252. 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
  253. 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
  254. 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
  255. 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
  256. 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
  257. 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
  258. 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
  259. 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
  260. 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
  261. 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
  262. 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
  263. 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
  264. 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
  265. 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
  266. 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
  267. 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
  268. 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
  269. 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
  270. 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
  271. 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
  272. 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
  273. 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
  274. 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
  275. 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
  276. 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
  277. 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
  278. 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
  279. 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
  280. 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
  281. 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
  282. 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
  283. 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
  284. 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
  285. 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
  286. 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
  287. 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
  288. 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
  289. 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
  290. 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
  291. 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
  292. 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
  293. 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
  294. 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
  295. 0x2d02ef8d,
  296. };
  297.  
  298. static Tcl_Obj *zipfs_literal_fstype=NULL;
  299. static Tcl_Obj *zipfs_literal_fsroot=NULL;
  300. static Tcl_Obj *zipfs_literal_fsseparator=NULL;
  301.  
  302.  
  303. /*
  304. *-------------------------------------------------------------------------
  305. *
  306. * ReadLock, WriteLock, Unlock --
  307. *
  308. * POSIX like rwlock functions to support multiple readers
  309. * and single writer on internal structs.
  310. *
  311. * Limitations:
  312. * - a read lock cannot be promoted to a write lock
  313. * - a write lock may not be nested
  314. *
  315. *-------------------------------------------------------------------------
  316. */
  317.  
  318. TCL_DECLARE_MUTEX(ZipFSMutex)
  319.  
  320. #ifdef TCL_THREADS
  321.  
  322. static Tcl_Condition ZipFSCond;
  323.  
  324. static void
  325. ReadLock(void)
  326. {
  327. Tcl_MutexLock(&ZipFSMutex);
  328. while (ZipFS.lock < 0) {
  329. ZipFS.waiters++;
  330. Tcl_ConditionWait(&ZipFSCond, &ZipFSMutex, NULL);
  331. ZipFS.waiters--;
  332. }
  333. ZipFS.lock++;
  334. Tcl_MutexUnlock(&ZipFSMutex);
  335. }
  336.  
  337. static void
  338. WriteLock(void)
  339. {
  340. Tcl_MutexLock(&ZipFSMutex);
  341. while (ZipFS.lock != 0) {
  342. ZipFS.waiters++;
  343. Tcl_ConditionWait(&ZipFSCond, &ZipFSMutex, NULL);
  344. ZipFS.waiters--;
  345. }
  346. ZipFS.lock = -1;
  347. Tcl_MutexUnlock(&ZipFSMutex);
  348. }
  349.  
  350. static void
  351. Unlock(void)
  352. {
  353. Tcl_MutexLock(&ZipFSMutex);
  354. if (ZipFS.lock > 0) {
  355. --ZipFS.lock;
  356. } else if (ZipFS.lock < 0) {
  357. ZipFS.lock = 0;
  358. }
  359. if ((ZipFS.lock == 0) && (ZipFS.waiters > 0)) {
  360. Tcl_ConditionNotify(&ZipFSCond);
  361. }
  362. Tcl_MutexUnlock(&ZipFSMutex);
  363. }
  364.  
  365. #else
  366.  
  367. #define ReadLock() do {} while (0)
  368. #define WriteLock() do {} while (0)
  369. #define Unlock() do {} while (0)
  370.  
  371. #endif
  372.  
  373. /*
  374. *-------------------------------------------------------------------------
  375. *
  376. * DosTimeDate, ToDosTime, ToDosDate --
  377. *
  378. * Functions to perform conversions between DOS time stamps
  379. * and POSIX time_t.
  380. *
  381. *-------------------------------------------------------------------------
  382. */
  383.  
  384. static time_t
  385. DosTimeDate(int dosDate, int dosTime)
  386. {
  387. struct tm tm;
  388. time_t ret;
  389.  
  390. memset(&tm, 0, sizeof (tm));
  391. tm.tm_year = (((dosDate & 0xfe00) >> 9) + 80);
  392. tm.tm_mon = ((dosDate & 0x1e0) >> 5) - 1;
  393. tm.tm_mday = dosDate & 0x1f;
  394. tm.tm_hour = (dosTime & 0xf800) >> 11;
  395. tm.tm_min = (dosTime & 0x7e) >> 5;
  396. tm.tm_sec = (dosTime & 0x1f) << 1;
  397. ret = mktime(&tm);
  398. if (ret == (time_t) -1) {
  399. /* fallback to 1980-01-01T00:00:00+00:00 (DOS epoch) */
  400. ret = (time_t) 315532800;
  401. }
  402. return ret;
  403. }
  404.  
  405. static int
  406. ToDosTime(time_t when)
  407. {
  408. struct tm *tmp, tm;
  409.  
  410. #ifdef TCL_THREADS
  411. #if defined(_WIN32) || defined(_WIN64)
  412. /* Win32 uses thread local storage */
  413. tmp = localtime(&when);
  414. tm = *tmp;
  415. #else
  416. #ifdef HAVE_LOCALTIME_R
  417. tmp = &tm;
  418. localtime_r(&when, tmp);
  419. #else
  420. Tcl_MutexLock(&localtimeMutex);
  421. tmp = localtime(&when);
  422. tm = *tmp;
  423. Tcl_MutexUnlock(&localtimeMutex);
  424. #endif
  425. #endif
  426. #else
  427. tmp = localtime(&when);
  428. tm = *tmp;
  429. #endif
  430. return (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1);
  431. }
  432.  
  433. static int
  434. ToDosDate(time_t when)
  435. {
  436. struct tm *tmp, tm;
  437.  
  438. #ifdef TCL_THREADS
  439. #if defined(_WIN32) || defined(_WIN64)
  440. /* Win32 uses thread local storage */
  441. tmp = localtime(&when);
  442. tm = *tmp;
  443. #else
  444. #ifdef HAVE_LOCALTIME_R
  445. tmp = &tm;
  446. localtime_r(&when, tmp);
  447. #else
  448. Tcl_MutexLock(&localtimeMutex);
  449. tmp = localtime(&when);
  450. tm = *tmp;
  451. Tcl_MutexUnlock(&localtimeMutex);
  452. #endif
  453. #endif
  454. #else
  455. tmp = localtime(&when);
  456. tm = *tmp;
  457. #endif
  458. return ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday;
  459. }
  460.  
  461. /*
  462. *-------------------------------------------------------------------------
  463. *
  464. * CountSlashes --
  465. *
  466. * This function counts the number of slashes in a pathname string.
  467. *
  468. * Results:
  469. * Number of slashes found in string.
  470. *
  471. * Side effects:
  472. * None.
  473. *
  474. *-------------------------------------------------------------------------
  475. */
  476.  
  477. static int
  478. CountSlashes(const char *string)
  479. {
  480. int count = 0;
  481. const char *p = string;
  482.  
  483. while (*p != '\0') {
  484. if (*p == '/') {
  485. count++;
  486. }
  487. p++;
  488. }
  489. return count;
  490. }
  491.  
  492. /*
  493. *-------------------------------------------------------------------------
  494. *
  495. * CanonicalPath --
  496. *
  497. * This function computes the canonical path from a directory
  498. * and file name components into the specified Tcl_DString.
  499. *
  500. * Results:
  501. * Returns the pointer to the canonical path contained in the
  502. * specified Tcl_DString.
  503. *
  504. * Side effects:
  505. * Modifies the specified Tcl_DString.
  506. *
  507. *-------------------------------------------------------------------------
  508. */
  509.  
  510. static char *
  511. CanonicalPath(const char *root, const char *tail, Tcl_DString *dsPtr,int ZIPFSPATH)
  512. {
  513. char *path;
  514. char *result;
  515. int i, j, c, isunc = 0, isvfs=0, n=0;
  516. #if HAS_DRIVES
  517. int zipfspath=1;
  518. if ((tail[0] != '\0') && (strchr(drvletters, tail[0]) != NULL) &&
  519. (tail[1] == ':')) {
  520. tail += 2;
  521. zipfspath=0;
  522. }
  523. /* UNC style path */
  524. if (tail[0] == '\\') {
  525. root = "";
  526. ++tail;
  527. zipfspath=0;
  528. }
  529. if (tail[0] == '\\') {
  530. root = "/";
  531. ++tail;
  532. zipfspath=0;
  533. }
  534. if(zipfspath) {
  535. #endif
  536. /* UNC style path */
  537. if(root && strncmp(root,ZIPFS_VOLUME,ZIPFS_VOLUME_LEN)==0) {
  538. isvfs=1;
  539. } else if (tail && strncmp(tail,ZIPFS_VOLUME,ZIPFS_VOLUME_LEN) == 0) {
  540. isvfs=2;
  541. }
  542. if(isvfs!=1) {
  543. if ((root[0] == '/') && (root[1] == '/')) {
  544. isunc = 1;
  545. }
  546. }
  547. #if HAS_DRIVES
  548. }
  549. #endif
  550. if(isvfs!=2) {
  551. if (tail[0] == '/') {
  552. if(isvfs!=1) {
  553. root = "";
  554. }
  555. ++tail;
  556. isunc = 0;
  557. }
  558. if (tail[0] == '/') {
  559. if(isvfs!=1) {
  560. root = "/";
  561. }
  562. ++tail;
  563. isunc = 1;
  564. }
  565. }
  566. i = strlen(root);
  567. j = strlen(tail);
  568. if(isvfs==1) {
  569. if(i>ZIPFS_VOLUME_LEN) {
  570. Tcl_DStringSetLength(dsPtr, i + j + 1);
  571. path = Tcl_DStringValue(dsPtr);
  572. memcpy(path, root, i);
  573. path[i++] = '/';
  574. memcpy(path + i, tail, j);
  575. } else {
  576. Tcl_DStringSetLength(dsPtr, i + j);
  577. path = Tcl_DStringValue(dsPtr);
  578. memcpy(path, root, i);
  579. memcpy(path + i, tail, j);
  580. }
  581. } else if(isvfs==2) {
  582. Tcl_DStringSetLength(dsPtr, j);
  583. path = Tcl_DStringValue(dsPtr);
  584. memcpy(path, tail, j);
  585. } else {
  586. if (ZIPFSPATH) {
  587. Tcl_DStringSetLength(dsPtr, i + j + ZIPFS_VOLUME_LEN);
  588. path = Tcl_DStringValue(dsPtr);
  589. memcpy(path, ZIPFS_VOLUME, ZIPFS_VOLUME_LEN);
  590. memcpy(path + ZIPFS_VOLUME_LEN + i , tail, j);
  591. } else {
  592. Tcl_DStringSetLength(dsPtr, i + j + 1);
  593. path = Tcl_DStringValue(dsPtr);
  594. memcpy(path, root, i);
  595. path[i++] = '/';
  596. memcpy(path + i, tail, j);
  597. }
  598. }
  599. #if HAS_DRIVES
  600. for (i = 0; path[i] != '\0'; i++) {
  601. if (path[i] == '\\') {
  602. path[i] = '/';
  603. }
  604. }
  605. #endif
  606. if(ZIPFSPATH) {
  607. n=ZIPFS_VOLUME_LEN;
  608. } else {
  609. n=0;
  610. }
  611. for (i = j = n; (c = path[i]) != '\0'; i++) {
  612. if (c == '/') {
  613. int c2 = path[i + 1];
  614. if (c2 == '/') {
  615. continue;
  616. }
  617. if (c2 == '.') {
  618. int c3 = path[i + 2];
  619. if ((c3 == '/') || (c3 == '\0')) {
  620. i++;
  621. continue;
  622. }
  623. if ((c3 == '.') &&
  624. ((path[i + 3] == '/') || (path [i + 3] == '\0'))) {
  625. i += 2;
  626. while ((j > 0) && (path[j - 1] != '/')) {
  627. j--;
  628. }
  629. if (j > isunc) {
  630. --j;
  631. while ((j > 1 + isunc) && (path[j - 2] == '/')) {
  632. j--;
  633. }
  634. }
  635. continue;
  636. }
  637. }
  638. }
  639. path[j++] = c;
  640. }
  641. if (j == 0) {
  642. path[j++] = '/';
  643. }
  644. path[j] = 0;
  645. Tcl_DStringSetLength(dsPtr, j);
  646. result=Tcl_DStringValue(dsPtr);
  647. return result;
  648. }
  649.  
  650.  
  651.  
  652. /*
  653. *-------------------------------------------------------------------------
  654. *
  655. * ZipFSLookup --
  656. *
  657. * This function returns the ZIP entry struct corresponding to
  658. * the ZIP archive member of the given file name.
  659. *
  660. * Results:
  661. * Returns the pointer to ZIP entry struct or NULL if the
  662. * the given file name could not be found in the global list
  663. * of ZIP archive members.
  664. *
  665. * Side effects:
  666. * None.
  667. *
  668. *-------------------------------------------------------------------------
  669. */
  670.  
  671. static ZipEntry *
  672. ZipFSLookup(char *filename)
  673. {
  674. Tcl_HashEntry *hPtr;
  675. ZipEntry *z;
  676. Tcl_DString ds;
  677. Tcl_DStringInit(&ds);
  678. hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, filename);
  679. z = hPtr ? (ZipEntry *) Tcl_GetHashValue(hPtr) : NULL;
  680. Tcl_DStringFree(&ds);
  681. return z;
  682. }
  683.  
  684. #ifdef NEVER_USED
  685.  
  686. /*
  687. *-------------------------------------------------------------------------
  688. *
  689. * ZipFSLookupMount --
  690. *
  691. * This function returns an indication if the given file name
  692. * corresponds to a mounted ZIP archive file.
  693. *
  694. * Results:
  695. * Returns true, if the given file name is a mounted ZIP archive file.
  696. *
  697. * Side effects:
  698. * None.
  699. *
  700. *-------------------------------------------------------------------------
  701. */
  702.  
  703. static int
  704. ZipFSLookupMount(char *filename)
  705. {
  706. Tcl_HashEntry *hPtr;
  707. Tcl_HashSearch search;
  708. ZipFile *zf;
  709. int match = 0;
  710. hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search);
  711. while (hPtr != NULL) {
  712. if ((zf = (ZipFile *) Tcl_GetHashValue(hPtr)) != NULL) {
  713. if (strcmp(zf->mntpt, filename) == 0) {
  714. match = 1;
  715. break;
  716. }
  717. }
  718. hPtr = Tcl_NextHashEntry(&search);
  719. }
  720. return match;
  721. }
  722. #endif
  723.  
  724. /*
  725. *-------------------------------------------------------------------------
  726. *
  727. * ZipFSCloseArchive --
  728. *
  729. * This function closes a mounted ZIP archive file.
  730. *
  731. * Results:
  732. * None.
  733. *
  734. * Side effects:
  735. * A memory mapped ZIP archive is unmapped, allocated memory is
  736. * released.
  737. *
  738. *-------------------------------------------------------------------------
  739. */
  740.  
  741. static void
  742. ZipFSCloseArchive(Tcl_Interp *interp, ZipFile *zf)
  743. {
  744. #if defined(_WIN32) || defined(_WIN64)
  745. if ((zf->data != NULL) && (zf->tofree == NULL)) {
  746. UnmapViewOfFile(zf->data);
  747. zf->data = NULL;
  748. }
  749. if (zf->mh != INVALID_HANDLE_VALUE) {
  750. CloseHandle(zf->mh);
  751. }
  752. #else
  753. if ((zf->data != MAP_FAILED) && (zf->tofree == NULL)) {
  754. munmap(zf->data, zf->length);
  755. zf->data = MAP_FAILED;
  756. }
  757. #endif
  758. if (zf->tofree != NULL) {
  759. Tcl_Free((char *) zf->tofree);
  760. zf->tofree = NULL;
  761. }
  762. Tcl_Close(interp, zf->chan);
  763. zf->chan = NULL;
  764. }
  765.  
  766. /*
  767. *-------------------------------------------------------------------------
  768. *
  769. * ZipFSOpenArchive --
  770. *
  771. * This function opens a ZIP archive file for reading. An attempt
  772. * is made to memory map that file. Otherwise it is read into
  773. * an allocated memory buffer. The ZIP archive header is verified
  774. * and must be valid for the function to succeed. When "needZip"
  775. * is zero an embedded ZIP archive in an executable file is accepted.
  776. *
  777. * Results:
  778. * TCL_OK on success, TCL_ERROR otherwise with an error message
  779. * placed into the given "interp" if it is not NULL.
  780. *
  781. * Side effects:
  782. * ZIP archive is memory mapped or read into allocated memory,
  783. * the given ZipFile struct is filled with information about
  784. * the ZIP archive file.
  785. *
  786. *-------------------------------------------------------------------------
  787. */
  788.  
  789. static int
  790. ZipFSOpenArchive(Tcl_Interp *interp, const char *zipname, int needZip,
  791. ZipFile *zf)
  792. {
  793. int i;
  794. ClientData handle;
  795. unsigned char *p, *q;
  796.  
  797. #if defined(_WIN32) || defined(_WIN64)
  798. zf->data = NULL;
  799. zf->mh = INVALID_HANDLE_VALUE;
  800. #else
  801. zf->data = MAP_FAILED;
  802. #endif
  803. zf->length = 0;
  804. zf->nfiles = 0;
  805. zf->baseoffs = zf->baseoffsp = 0;
  806. zf->tofree = NULL;
  807. zf->pwbuf[0] = 0;
  808. zf->chan = Tcl_OpenFileChannel(interp, zipname, "r", 0);
  809. if (zf->chan == NULL) {
  810. return TCL_ERROR;
  811. }
  812. if (Tcl_GetChannelHandle(zf->chan, TCL_READABLE, &handle) != TCL_OK) {
  813. if (Tcl_SetChannelOption(interp, zf->chan, "-translation", "binary")
  814. != TCL_OK) {
  815. goto error;
  816. }
  817. if (Tcl_SetChannelOption(interp, zf->chan, "-encoding", "binary")
  818. != TCL_OK) {
  819. goto error;
  820. }
  821. zf->length = Tcl_Seek(zf->chan, 0, SEEK_END);
  822. if ((zf->length <= 0) || (zf->length > 64 * 1024 * 1024)) {
  823. if (interp) {
  824. Tcl_SetObjResult(interp,
  825. Tcl_NewStringObj("illegal file size", -1));
  826. }
  827. goto error;
  828. }
  829. Tcl_Seek(zf->chan, 0, SEEK_SET);
  830. zf->tofree = zf->data = (unsigned char *) Tcl_AttemptAlloc(zf->length);
  831. if (zf->tofree == NULL) {
  832. if (interp) {
  833. Tcl_SetObjResult(interp,
  834. Tcl_NewStringObj("out of memory", -1));
  835. }
  836. goto error;
  837. }
  838. i = Tcl_Read(zf->chan, (char *) zf->data, zf->length);
  839. if (i != zf->length) {
  840. if (interp) {
  841. Tcl_SetObjResult(interp,
  842. Tcl_NewStringObj("file read error", -1));
  843. }
  844. goto error;
  845. }
  846. Tcl_Close(interp, zf->chan);
  847. zf->chan = NULL;
  848. } else {
  849. #if defined(_WIN32) || defined(_WIN64)
  850. zf->length = GetFileSize((HANDLE) handle, 0);
  851. if ((zf->length == INVALID_FILE_SIZE) ||
  852. (zf->length < ZIP_CENTRAL_END_LEN)) {
  853. if (interp != NULL) {
  854. Tcl_SetObjResult(interp,
  855. Tcl_NewStringObj("invalid file size", -1));
  856. }
  857. goto error;
  858. }
  859. zf->mh = CreateFileMapping((HANDLE) handle, 0, PAGE_READONLY, 0,
  860. zf->length, 0);
  861. if (zf->mh == INVALID_HANDLE_VALUE) {
  862. if (interp != NULL) {
  863. Tcl_SetObjResult(interp,
  864. Tcl_NewStringObj("file mapping failed", -1));
  865. }
  866. goto error;
  867. }
  868. zf->data = MapViewOfFile(zf->mh, FILE_MAP_READ, 0, 0, zf->length);
  869. if (zf->data == NULL) {
  870. if (interp != NULL) {
  871. Tcl_SetObjResult(interp,
  872. Tcl_NewStringObj("file mapping failed", -1));
  873. }
  874. goto error;
  875. }
  876. #else
  877. zf->length = lseek((int) (long) handle, 0, SEEK_END);
  878. if ((zf->length == -1) || (zf->length < ZIP_CENTRAL_END_LEN)) {
  879. if (interp != NULL) {
  880. Tcl_SetObjResult(interp,
  881. Tcl_NewStringObj("invalid file size", -1));
  882. }
  883. goto error;
  884. }
  885. lseek((int) (long) handle, 0, SEEK_SET);
  886. zf->data = (unsigned char *) mmap(0, zf->length, PROT_READ,
  887. MAP_FILE | MAP_PRIVATE,
  888. (int) (long) handle, 0);
  889. if (zf->data == MAP_FAILED) {
  890. if (interp != NULL) {
  891. Tcl_SetObjResult(interp,
  892. Tcl_NewStringObj("file mapping failed", -1));
  893. }
  894. goto error;
  895. }
  896. #endif
  897. }
  898. p = zf->data + zf->length - ZIP_CENTRAL_END_LEN;
  899. while (p >= zf->data) {
  900. if (*p == (ZIP_CENTRAL_END_SIG & 0xFF)) {
  901. if (zip_read_int(p) == ZIP_CENTRAL_END_SIG) {
  902. break;
  903. }
  904. p -= ZIP_SIG_LEN;
  905. } else {
  906. --p;
  907. }
  908. }
  909. if (p < zf->data) {
  910. if (!needZip) {
  911. zf->baseoffs = zf->baseoffsp = zf->length;
  912. return TCL_OK;
  913. }
  914. if (interp != NULL) {
  915. Tcl_SetObjResult(interp,
  916. Tcl_NewStringObj("wrong end signature", -1));
  917. }
  918. goto error;
  919. }
  920. zf->nfiles = zip_read_short(p + ZIP_CENTRAL_ENTS_OFFS);
  921. if (zf->nfiles == 0) {
  922. if (!needZip) {
  923. zf->baseoffs = zf->baseoffsp = zf->length;
  924. return TCL_OK;
  925. }
  926. if (interp != NULL) {
  927. Tcl_SetObjResult(interp,
  928. Tcl_NewStringObj("empty archive", -1));
  929. }
  930. goto error;
  931. }
  932. q = zf->data + zip_read_int(p + ZIP_CENTRAL_DIRSTART_OFFS);
  933. p -= zip_read_int(p + ZIP_CENTRAL_DIRSIZE_OFFS);
  934. if ((p < zf->data) || (p > (zf->data + zf->length)) ||
  935. (q < zf->data) || (q > (zf->data + zf->length))) {
  936. if (!needZip) {
  937. zf->baseoffs = zf->baseoffsp = zf->length;
  938. return TCL_OK;
  939. }
  940. if (interp != NULL) {
  941. Tcl_SetObjResult(interp,
  942. Tcl_NewStringObj("archive directory not found", -1));
  943. }
  944. goto error;
  945. }
  946. zf->baseoffs = zf->baseoffsp = p - q;
  947. zf->centoffs = p - zf->data;
  948. q = p;
  949. for (i = 0; i < zf->nfiles; i++) {
  950. int pathlen, comlen, extra;
  951.  
  952. if ((q + ZIP_CENTRAL_HEADER_LEN) > (zf->data + zf->length)) {
  953. if (interp != NULL) {
  954. Tcl_SetObjResult(interp,
  955. Tcl_NewStringObj("wrong header length", -1));
  956. }
  957. goto error;
  958. }
  959. if (zip_read_int(q) != ZIP_CENTRAL_HEADER_SIG) {
  960. if (interp != NULL) {
  961. Tcl_SetObjResult(interp,
  962. Tcl_NewStringObj("wrong header signature", -1));
  963. }
  964. goto error;
  965. }
  966. pathlen = zip_read_short(q + ZIP_CENTRAL_PATHLEN_OFFS);
  967. comlen = zip_read_short(q + ZIP_CENTRAL_FCOMMENTLEN_OFFS);
  968. extra = zip_read_short(q + ZIP_CENTRAL_EXTRALEN_OFFS);
  969. q += pathlen + comlen + extra + ZIP_CENTRAL_HEADER_LEN;
  970. }
  971. q = zf->data + zf->baseoffs;
  972. if ((zf->baseoffs >= 6) &&
  973. (zip_read_int(q - 4) == ZIP_PASSWORD_END_SIG)) {
  974. i = q[-5];
  975. if (q - 5 - i > zf->data) {
  976. zf->pwbuf[0] = i;
  977. memcpy(zf->pwbuf + 1, q - 5 - i, i);
  978. zf->baseoffsp -= i ? (5 + i) : 0;
  979. }
  980. }
  981. return TCL_OK;
  982.  
  983. error:
  984. ZipFSCloseArchive(interp, zf);
  985. return TCL_ERROR;
  986. }
  987.  
  988. /*
  989. *-------------------------------------------------------------------------
  990. *
  991. * TclZipfs_Mount --
  992. *
  993. * This procedure is invoked to mount a given ZIP archive file on
  994. * a given mountpoint with optional ZIP password.
  995. *
  996. * Results:
  997. * A standard Tcl result.
  998. *
  999. * Side effects:
  1000. * A ZIP archive file is read, analyzed and mounted, resources are
  1001. * allocated.
  1002. *
  1003. *-------------------------------------------------------------------------
  1004. */
  1005.  
  1006. int
  1007. TclZipfs_Mount(Tcl_Interp *interp, const char *zipname, const char *mntpt,
  1008. const char *passwd)
  1009. {
  1010. int i, pwlen, isNew;
  1011. ZipFile *zf, zf0;
  1012. ZipEntry *z;
  1013. Tcl_HashEntry *hPtr;
  1014. Tcl_DString ds, fpBuf;
  1015. unsigned char *q;
  1016.  
  1017. ReadLock();
  1018. if (!ZipFS.initialized) {
  1019. if (interp != NULL) {
  1020. Tcl_SetObjResult(interp,
  1021. Tcl_NewStringObj("not initialized", -1));
  1022. }
  1023. Unlock();
  1024. return TCL_ERROR;
  1025. }
  1026. if (zipname == NULL) {
  1027. Tcl_HashSearch search;
  1028. int ret = TCL_OK;
  1029.  
  1030. i = 0;
  1031. hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search);
  1032. while (hPtr != NULL) {
  1033. if ((zf = (ZipFile *) Tcl_GetHashValue(hPtr)) != NULL) {
  1034. if (interp != NULL) {
  1035. Tcl_AppendElement(interp, zf->mntpt);
  1036. Tcl_AppendElement(interp, zf->name);
  1037. }
  1038. ++i;
  1039. }
  1040. hPtr = Tcl_NextHashEntry(&search);
  1041. }
  1042. if (interp == NULL) {
  1043. ret = (i > 0) ? TCL_OK : TCL_BREAK;
  1044. }
  1045. Unlock();
  1046. return ret;
  1047. }
  1048. if (mntpt == NULL) {
  1049. if (interp == NULL) {
  1050. Unlock();
  1051. return TCL_OK;
  1052. }
  1053. hPtr = Tcl_FindHashEntry(&ZipFS.zipHash, zipname);
  1054. if (hPtr != NULL) {
  1055. if ((zf = Tcl_GetHashValue(hPtr)) != NULL) {
  1056. Tcl_SetObjResult(interp,
  1057. Tcl_NewStringObj(zf->mntpt, zf->mntptlen));
  1058. }
  1059. }
  1060. Unlock();
  1061. return TCL_OK;
  1062. }
  1063. Unlock();
  1064. pwlen = 0;
  1065. if (passwd != NULL) {
  1066. pwlen = strlen(passwd);
  1067. if ((pwlen > 255) || (strchr(passwd, 0xff) != NULL)) {
  1068. if (interp) {
  1069. Tcl_SetObjResult(interp,
  1070. Tcl_NewStringObj("illegal password", -1));
  1071. }
  1072. return TCL_ERROR;
  1073. }
  1074. }
  1075. if (ZipFSOpenArchive(interp, zipname, 1, &zf0) != TCL_OK) {
  1076. return TCL_ERROR;
  1077. }
  1078. /*
  1079. * Mount point can come from Tcl_GetNameOfExecutable()
  1080. * which sometimes is a relative or otherwise denormalized path.
  1081. * But an absolute name is needed as mount point here.
  1082. */
  1083. WriteLock();
  1084. hPtr = Tcl_CreateHashEntry(&ZipFS.zipHash, zipname, &isNew);
  1085. if (!isNew) {
  1086. zf = (ZipFile *) Tcl_GetHashValue(hPtr);
  1087. if (interp != NULL) {
  1088. Tcl_AppendResult(interp, "already mounted on \"", zf->mntptlen ?
  1089. zf->mntpt : "/", "\"", (char *) NULL);
  1090. }
  1091. Unlock();
  1092. ZipFSCloseArchive(interp, &zf0);
  1093. return TCL_ERROR;
  1094. }
  1095. if (strcmp(mntpt, "/") == 0) {
  1096. mntpt = "";
  1097. }
  1098. zf = (ZipFile *) Tcl_AttemptAlloc(sizeof (*zf) + strlen(mntpt) + 1);
  1099. if (zf == NULL) {
  1100. if (interp != NULL) {
  1101. Tcl_AppendResult(interp, "out of memory", (char *) NULL);
  1102. }
  1103. Unlock();
  1104. ZipFSCloseArchive(interp, &zf0);
  1105. return TCL_ERROR;
  1106. }
  1107. *zf = zf0;
  1108. zf->name = Tcl_GetHashKey(&ZipFS.zipHash, hPtr);
  1109. strcpy(zf->mntpt, mntpt);
  1110. zf->mntptlen = strlen(zf->mntpt);
  1111. zf->entries = NULL;
  1112. zf->topents = NULL;
  1113. zf->nopen = 0;
  1114. Tcl_SetHashValue(hPtr, (ClientData) zf);
  1115. if ((zf->pwbuf[0] == 0) && pwlen) {
  1116. int k = 0;
  1117. i = pwlen;
  1118. zf->pwbuf[k++] = i;
  1119. while (i > 0) {
  1120. zf->pwbuf[k] = (passwd[i - 1] & 0x0f) |
  1121. pwrot[(passwd[i - 1] >> 4) & 0x0f];
  1122. k++;
  1123. i--;
  1124. }
  1125. zf->pwbuf[k] = '\0';
  1126. }
  1127. if (mntpt[0] != '\0') {
  1128. z = (ZipEntry *) Tcl_Alloc(sizeof (*z));
  1129. z->name = NULL;
  1130. z->tnext = NULL;
  1131. z->depth = CountSlashes(mntpt);
  1132. z->zipfile = zf;
  1133. z->isdir = 1;
  1134. z->isenc = 0;
  1135. z->offset = zf->baseoffs;
  1136. z->crc32 = 0;
  1137. z->timestamp = 0;
  1138. z->nbyte = z->nbytecompr = 0;
  1139. z->cmeth = ZIP_COMPMETH_STORED;
  1140. z->data = NULL;
  1141. hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, mntpt, &isNew);
  1142. if (!isNew) {
  1143. /* skip it */
  1144. Tcl_Free((char *) z);
  1145. } else {
  1146. Tcl_SetHashValue(hPtr, (ClientData) z);
  1147. z->name = Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
  1148. z->next = zf->entries;
  1149. zf->entries = z;
  1150. }
  1151. }
  1152. q = zf->data + zf->centoffs;
  1153. Tcl_DStringInit(&fpBuf);
  1154. Tcl_DStringInit(&ds);
  1155. for (i = 0; i < zf->nfiles; i++) {
  1156. int pathlen, comlen, extra, isdir = 0, dosTime, dosDate, nbcompr, offs;
  1157. unsigned char *lq, *gq = NULL;
  1158. char *fullpath, *path;
  1159.  
  1160. pathlen = zip_read_short(q + ZIP_CENTRAL_PATHLEN_OFFS);
  1161. comlen = zip_read_short(q + ZIP_CENTRAL_FCOMMENTLEN_OFFS);
  1162. extra = zip_read_short(q + ZIP_CENTRAL_EXTRALEN_OFFS);
  1163. Tcl_DStringSetLength(&ds, 0);
  1164. Tcl_DStringAppend(&ds, (char *) q + ZIP_CENTRAL_HEADER_LEN, pathlen);
  1165. path = Tcl_DStringValue(&ds);
  1166. if ((pathlen > 0) && (path[pathlen - 1] == '/')) {
  1167. Tcl_DStringSetLength(&ds, pathlen - 1);
  1168. path = Tcl_DStringValue(&ds);
  1169. isdir = 1;
  1170. }
  1171. if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) {
  1172. goto nextent;
  1173. }
  1174. lq = zf->data + zf->baseoffs +
  1175. zip_read_int(q + ZIP_CENTRAL_LOCALHDR_OFFS);
  1176. if ((lq < zf->data) || (lq > (zf->data + zf->length))) {
  1177. goto nextent;
  1178. }
  1179. nbcompr = zip_read_int(lq + ZIP_LOCAL_COMPLEN_OFFS);
  1180. if (!isdir && (nbcompr == 0) &&
  1181. (zip_read_int(lq + ZIP_LOCAL_UNCOMPLEN_OFFS) == 0) &&
  1182. (zip_read_int(lq + ZIP_LOCAL_CRC32_OFFS) == 0)) {
  1183. gq = q;
  1184. nbcompr = zip_read_int(gq + ZIP_CENTRAL_COMPLEN_OFFS);
  1185. }
  1186. offs = (lq - zf->data)
  1187. + ZIP_LOCAL_HEADER_LEN
  1188. + zip_read_short(lq + ZIP_LOCAL_PATHLEN_OFFS)
  1189. + zip_read_short(lq + ZIP_LOCAL_EXTRALEN_OFFS);
  1190. if ((offs + nbcompr) > zf->length) {
  1191. goto nextent;
  1192. }
  1193. if (!isdir && (mntpt[0] == '\0') && !CountSlashes(path)) {
  1194. #ifdef ANDROID
  1195. /*
  1196. * When mounting the ZIP archive on the root directory try
  1197. * to remap top level regular files of the archive to
  1198. * /assets/.root/... since this directory should not be
  1199. * in a valid APK due to the leading dot in the file name
  1200. * component. This trick should make the files
  1201. * AndroidManifest.xml, resources.arsc, and classes.dex
  1202. * visible to Tcl.
  1203. */
  1204. Tcl_DString ds2;
  1205.  
  1206. Tcl_DStringInit(&ds2);
  1207. Tcl_DStringAppend(&ds2, "assets/.root/", -1);
  1208. Tcl_DStringAppend(&ds2, path, -1);
  1209. hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, Tcl_DStringValue(&ds2));
  1210. if (hPtr != NULL) {
  1211. /* should not happen but skip it anyway */
  1212. Tcl_DStringFree(&ds2);
  1213. goto nextent;
  1214. }
  1215. Tcl_DStringSetLength(&ds, 0);
  1216. Tcl_DStringAppend(&ds, Tcl_DStringValue(&ds2),
  1217. Tcl_DStringLength(&ds2));
  1218. path = Tcl_DStringValue(&ds);
  1219. Tcl_DStringFree(&ds2);
  1220. #else
  1221. /*
  1222. * Regular files skipped when mounting on root.
  1223. */
  1224. goto nextent;
  1225. #endif
  1226. }
  1227. Tcl_DStringSetLength(&fpBuf, 0);
  1228. fullpath = CanonicalPath(mntpt, path, &fpBuf, 1);
  1229. z = (ZipEntry *) Tcl_Alloc(sizeof (*z));
  1230. z->name = NULL;
  1231. z->tnext = NULL;
  1232. z->depth = CountSlashes(fullpath);
  1233. z->zipfile = zf;
  1234. z->isdir = isdir;
  1235. z->isenc = (zip_read_short(lq + ZIP_LOCAL_FLAGS_OFFS) & 1)
  1236. && (nbcompr > 12);
  1237. z->offset = offs;
  1238. if (gq != NULL) {
  1239. z->crc32 = zip_read_int(gq + ZIP_CENTRAL_CRC32_OFFS);
  1240. dosDate = zip_read_short(gq + ZIP_CENTRAL_MDATE_OFFS);
  1241. dosTime = zip_read_short(gq + ZIP_CENTRAL_MTIME_OFFS);
  1242. z->timestamp = DosTimeDate(dosDate, dosTime);
  1243. z->nbyte = zip_read_int(gq + ZIP_CENTRAL_UNCOMPLEN_OFFS);
  1244. z->cmeth = zip_read_short(gq + ZIP_CENTRAL_COMPMETH_OFFS);
  1245. } else {
  1246. z->crc32 = zip_read_int(lq + ZIP_LOCAL_CRC32_OFFS);
  1247. dosDate = zip_read_short(lq + ZIP_LOCAL_MDATE_OFFS);
  1248. dosTime = zip_read_short(lq + ZIP_LOCAL_MTIME_OFFS);
  1249. z->timestamp = DosTimeDate(dosDate, dosTime);
  1250. z->nbyte = zip_read_int(lq + ZIP_LOCAL_UNCOMPLEN_OFFS);
  1251. z->cmeth = zip_read_short(lq + ZIP_LOCAL_COMPMETH_OFFS);
  1252. }
  1253. z->nbytecompr = nbcompr;
  1254. z->data = NULL;
  1255. hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, fullpath, &isNew);
  1256. if (!isNew) {
  1257. /* should not happen but skip it anyway */
  1258. Tcl_Free((char *) z);
  1259. } else {
  1260. Tcl_SetHashValue(hPtr, (ClientData) z);
  1261. z->name = Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
  1262. z->next = zf->entries;
  1263. zf->entries = z;
  1264. if (isdir && (mntpt[0] == '\0') && (z->depth == 1)) {
  1265. z->tnext = zf->topents;
  1266. zf->topents = z;
  1267. }
  1268. if (!z->isdir && (z->depth > 1)) {
  1269. char *dir, *end;
  1270. ZipEntry *zd;
  1271.  
  1272. Tcl_DStringSetLength(&ds, strlen(z->name) + 8);
  1273. Tcl_DStringSetLength(&ds, 0);
  1274. Tcl_DStringAppend(&ds, z->name, -1);
  1275. dir = Tcl_DStringValue(&ds);
  1276. end = strrchr(dir, '/');
  1277. while ((end != NULL) && (end != dir)) {
  1278. Tcl_DStringSetLength(&ds, end - dir);
  1279. hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, dir);
  1280. if (hPtr != NULL) {
  1281. break;
  1282. }
  1283. zd = (ZipEntry *) Tcl_Alloc(sizeof (*zd));
  1284. zd->name = NULL;
  1285. zd->tnext = NULL;
  1286. zd->depth = CountSlashes(dir);
  1287. zd->zipfile = zf;
  1288. zd->isdir = 1;
  1289. zd->isenc = 0;
  1290. zd->offset = z->offset;
  1291. zd->crc32 = 0;
  1292. zd->timestamp = z->timestamp;
  1293. zd->nbyte = zd->nbytecompr = 0;
  1294. zd->cmeth = ZIP_COMPMETH_STORED;
  1295. zd->data = NULL;
  1296. hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, dir, &isNew);
  1297. if (!isNew) {
  1298. /* should not happen but skip it anyway */
  1299. Tcl_Free((char *) zd);
  1300. } else {
  1301. Tcl_SetHashValue(hPtr, (ClientData) zd);
  1302. zd->name = Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
  1303. zd->next = zf->entries;
  1304. zf->entries = zd;
  1305. if ((mntpt[0] == '\0') && (zd->depth == 1)) {
  1306. zd->tnext = zf->topents;
  1307. zf->topents = zd;
  1308. }
  1309. }
  1310. end = strrchr(dir, '/');
  1311. }
  1312. }
  1313. }
  1314. nextent:
  1315. q += pathlen + comlen + extra + ZIP_CENTRAL_HEADER_LEN;
  1316. }
  1317. Unlock();
  1318. Tcl_DStringFree(&fpBuf);
  1319. Tcl_DStringFree(&ds);
  1320. Tcl_FSMountsChanged(NULL);
  1321. return TCL_OK;
  1322. }
  1323.  
  1324. /*
  1325. *-------------------------------------------------------------------------
  1326. *
  1327. * TclZipfs_Unmount --
  1328. *
  1329. * This procedure is invoked to unmount a given ZIP archive.
  1330. *
  1331. * Results:
  1332. * A standard Tcl result.
  1333. *
  1334. * Side effects:
  1335. * A mounted ZIP archive file is unmounted, resources are free'd.
  1336. *
  1337. *-------------------------------------------------------------------------
  1338. */
  1339.  
  1340. int
  1341. TclZipfs_Unmount(Tcl_Interp *interp, const char *zipname)
  1342. {
  1343. ZipFile *zf;
  1344. ZipEntry *z, *znext;
  1345. Tcl_HashEntry *hPtr;
  1346. int ret = TCL_OK, unmounted = 0;
  1347.  
  1348. WriteLock();
  1349. if (!ZipFS.initialized) {
  1350. goto done;
  1351. }
  1352. hPtr = Tcl_FindHashEntry(&ZipFS.zipHash, zipname);
  1353. if (hPtr == NULL) {
  1354. /* don't report error */
  1355. goto done;
  1356. }
  1357. zf = (ZipFile *) Tcl_GetHashValue(hPtr);
  1358. if (zf->nopen > 0) {
  1359. if (interp != NULL) {
  1360. Tcl_SetObjResult(interp,
  1361. Tcl_NewStringObj("filesystem is busy", -1));
  1362. }
  1363. ret = TCL_ERROR;
  1364. goto done;
  1365. }
  1366. Tcl_DeleteHashEntry(hPtr);
  1367. for (z = zf->entries; z; z = znext) {
  1368. znext = z->next;
  1369. hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, z->name);
  1370. if (hPtr) {
  1371. Tcl_DeleteHashEntry(hPtr);
  1372. }
  1373. if (z->data != NULL) {
  1374. Tcl_Free((char *) z->data);
  1375. }
  1376. Tcl_Free((char *) z);
  1377. }
  1378. ZipFSCloseArchive(interp, zf);
  1379. Tcl_Free((char *) zf);
  1380. unmounted = 1;
  1381. done:
  1382. Unlock();
  1383. if (unmounted) {
  1384. Tcl_FSMountsChanged(NULL);
  1385. }
  1386. return ret;
  1387. }
  1388.  
  1389. /*
  1390. *-------------------------------------------------------------------------
  1391. *
  1392. * ZipFSMountObjCmd --
  1393. *
  1394. * This procedure is invoked to process the "zipfs::mount" command.
  1395. *
  1396. * Results:
  1397. * A standard Tcl result.
  1398. *
  1399. * Side effects:
  1400. * A ZIP archive file is mounted, resources are allocated.
  1401. *
  1402. *-------------------------------------------------------------------------
  1403. */
  1404.  
  1405. static int
  1406. ZipFSMountObjCmd(ClientData clientData, Tcl_Interp *interp,
  1407. int objc, Tcl_Obj *const objv[])
  1408. {
  1409. if (objc > 4) {
  1410. Tcl_WrongNumArgs(interp, 1, objv,
  1411. "?zipfile? ?mountpoint? ?password?");
  1412. return TCL_ERROR;
  1413. }
  1414. return TclZipfs_Mount(interp, (objc > 1) ? Tcl_GetString(objv[1]) : NULL,
  1415. (objc > 2) ? Tcl_GetString(objv[2]) : NULL,
  1416. (objc > 3) ? Tcl_GetString(objv[3]) : NULL);
  1417. }
  1418.  
  1419. /*
  1420. *-------------------------------------------------------------------------
  1421. *
  1422. * ZipFSRootObjCmd --
  1423. *
  1424. * This procedure is invoked to process the "zipfs::root" command. It
  1425. * returns the root that all zipfs file systems are mounted under.
  1426. *
  1427. * Results:
  1428. * A standard Tcl result.
  1429. *
  1430. * Side effects:
  1431. *
  1432. *-------------------------------------------------------------------------
  1433. */
  1434.  
  1435. static int
  1436. ZipFSRootObjCmd(ClientData clientData, Tcl_Interp *interp,
  1437. int objc, Tcl_Obj *const objv[])
  1438. {
  1439. if(!zipfs_literal_fsroot) {
  1440. zipfs_literal_fsroot=Tcl_NewStringObj(ZIPFS_VOLUME, -1);
  1441. Tcl_IncrRefCount(zipfs_literal_fsroot);
  1442. }
  1443. Tcl_IncrRefCount(zipfs_literal_fsroot);
  1444. Tcl_SetObjResult(interp,zipfs_literal_fsroot);
  1445. return TCL_OK;
  1446. }
  1447.  
  1448. /*
  1449. *-------------------------------------------------------------------------
  1450. *
  1451. * ZipFSUnmountObjCmd --
  1452. *
  1453. * This procedure is invoked to process the "zipfs::unmount" command.
  1454. *
  1455. * Results:
  1456. * A standard Tcl result.
  1457. *
  1458. * Side effects:
  1459. * A mounted ZIP archive file is unmounted, resources are free'd.
  1460. *
  1461. *-------------------------------------------------------------------------
  1462. */
  1463.  
  1464. static int
  1465. ZipFSUnmountObjCmd(ClientData clientData, Tcl_Interp *interp,
  1466. int objc, Tcl_Obj *const objv[])
  1467. {
  1468. if (objc != 2) {
  1469. Tcl_WrongNumArgs(interp, 1, objv, "zipfile");
  1470. return TCL_ERROR;
  1471. }
  1472. return TclZipfs_Unmount(interp, Tcl_GetString(objv[1]));
  1473. }
  1474.  
  1475. /*
  1476. *-------------------------------------------------------------------------
  1477. *
  1478. * ZipFSMkKeyObjCmd --
  1479. *
  1480. * This procedure is invoked to process the "zipfs::mkkey" command.
  1481. * It produces a rotated password to be embedded into an image file.
  1482. *
  1483. * Results:
  1484. * A standard Tcl result.
  1485. *
  1486. * Side effects:
  1487. * None.
  1488. *
  1489. *-------------------------------------------------------------------------
  1490. */
  1491.  
  1492. static int
  1493. ZipFSMkKeyObjCmd(ClientData clientData, Tcl_Interp *interp,
  1494. int objc, Tcl_Obj *const objv[])
  1495. {
  1496. int len, i = 0;
  1497. char *pw, pwbuf[264];
  1498.  
  1499. if (objc != 2) {
  1500. Tcl_WrongNumArgs(interp, 1, objv, "password");
  1501. return TCL_ERROR;
  1502. }
  1503. pw = Tcl_GetString(objv[1]);
  1504. len = strlen(pw);
  1505. if (len == 0) {
  1506. return TCL_OK;
  1507. }
  1508. if ((len > 255) || (strchr(pw, 0xff) != NULL)) {
  1509. Tcl_SetObjResult(interp,
  1510. Tcl_NewStringObj("illegal password", -1));
  1511. return TCL_ERROR;
  1512. }
  1513. while (len > 0) {
  1514. int ch = pw[len - 1];
  1515.  
  1516. pwbuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
  1517. i++;
  1518. len--;
  1519. }
  1520. pwbuf[i] = i;
  1521. ++i;
  1522. pwbuf[i++] = (char) ZIP_PASSWORD_END_SIG;
  1523. pwbuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 8);
  1524. pwbuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 16);
  1525. pwbuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 24);
  1526. pwbuf[i] = '\0';
  1527. Tcl_AppendResult(interp, pwbuf, (char *) NULL);
  1528. return TCL_OK;
  1529. }
  1530.  
  1531. /*
  1532. *-------------------------------------------------------------------------
  1533. *
  1534. * ZipAddFile --
  1535. *
  1536. * This procedure is used by ZipFSMkZipOrImgCmd() to add a single
  1537. * file to the output ZIP archive file being written. A ZipEntry
  1538. * struct about the input file is added to the given fileHash table
  1539. * for later creation of the central ZIP directory.
  1540. *
  1541. * Results:
  1542. * A standard Tcl result.
  1543. *
  1544. * Side effects:
  1545. * Input file is read and (compressed and) written to the output
  1546. * ZIP archive file.
  1547. *
  1548. *-------------------------------------------------------------------------
  1549. */
  1550.  
  1551. static int
  1552. ZipAddFile(Tcl_Interp *interp, const char *path, const char *name,
  1553. Tcl_Channel out, const char *passwd,
  1554. char *buf, int bufsize, Tcl_HashTable *fileHash)
  1555. {
  1556. Tcl_Channel in;
  1557. Tcl_HashEntry *hPtr;
  1558. ZipEntry *z;
  1559. z_stream stream;
  1560. const char *zpath;
  1561. int nbyte, nbytecompr, len, crc, flush, pos[3], zpathlen, olen;
  1562. int mtime = 0, isNew, align = 0, cmeth;
  1563. unsigned long keys[3], keys0[3];
  1564. char obuf[4096];
  1565.  
  1566. zpath = name;
  1567. while (zpath != NULL && zpath[0] == '/') {
  1568. zpath++;
  1569. }
  1570. if ((zpath == NULL) || (zpath[0] == '\0')) {
  1571. return TCL_OK;
  1572. }
  1573. zpathlen = strlen(zpath);
  1574. if (zpathlen + ZIP_CENTRAL_HEADER_LEN > bufsize) {
  1575. Tcl_AppendResult(interp, "path too long for \"", path, "\"",
  1576. (char *) NULL);
  1577. return TCL_ERROR;
  1578. }
  1579. in = Tcl_OpenFileChannel(interp, path, "r", 0);
  1580. if ((in == NULL) ||
  1581. (Tcl_SetChannelOption(interp, in, "-translation", "binary")
  1582. != TCL_OK) ||
  1583. (Tcl_SetChannelOption(interp, in, "-encoding", "binary")
  1584. != TCL_OK)) {
  1585. #if defined(_WIN32) || defined(_WIN64)
  1586. /* hopefully a directory */
  1587. if (strcmp("permission denied", Tcl_PosixError(interp)) == 0) {
  1588. Tcl_Close(interp, in);
  1589. return TCL_OK;
  1590. }
  1591. #endif
  1592. Tcl_Close(interp, in);
  1593. return TCL_ERROR;
  1594. } else {
  1595. Tcl_Obj *pathObj = Tcl_NewStringObj(path, -1);
  1596. Tcl_StatBuf statBuf;
  1597.  
  1598. Tcl_IncrRefCount(pathObj);
  1599. if (Tcl_FSStat(pathObj, &statBuf) != -1) {
  1600. mtime = statBuf.st_mtime;
  1601. }
  1602. Tcl_DecrRefCount(pathObj);
  1603. }
  1604. Tcl_ResetResult(interp);
  1605. crc = 0;
  1606. nbyte = nbytecompr = 0;
  1607. while ((len = Tcl_Read(in, buf, bufsize)) > 0) {
  1608. crc = crc32(crc, (unsigned char *) buf, len);
  1609. nbyte += len;
  1610. }
  1611. if (len == -1) {
  1612. if (nbyte == 0) {
  1613. if (strcmp("illegal operation on a directory",
  1614. Tcl_PosixError(interp)) == 0) {
  1615. Tcl_Close(interp, in);
  1616. return TCL_OK;
  1617. }
  1618. }
  1619. Tcl_AppendResult(interp, "read error on \"", path, "\"",
  1620. (char *) NULL);
  1621. Tcl_Close(interp, in);
  1622. return TCL_ERROR;
  1623. }
  1624. if (Tcl_Seek(in, 0, SEEK_SET) == -1) {
  1625. Tcl_AppendResult(interp, "seek error on \"", path, "\"",
  1626. (char *) NULL);
  1627. Tcl_Close(interp, in);
  1628. return TCL_ERROR;
  1629. }
  1630. pos[0] = Tcl_Tell(out);
  1631. memset(buf, '\0', ZIP_LOCAL_HEADER_LEN);
  1632. memcpy(buf + ZIP_LOCAL_HEADER_LEN, zpath, zpathlen);
  1633. len = zpathlen + ZIP_LOCAL_HEADER_LEN;
  1634. if (Tcl_Write(out, buf, len) != len) {
  1635. wrerr:
  1636. Tcl_AppendResult(interp, "write error", (char *) NULL);
  1637. Tcl_Close(interp, in);
  1638. return TCL_ERROR;
  1639. }
  1640. if ((len + pos[0]) & 3) {
  1641. char abuf[8];
  1642.  
  1643. /*
  1644. * Align payload to next 4-byte boundary using a dummy extra
  1645. * entry similar to the zipalign tool from Android's SDK.
  1646. */
  1647. align = 4 + ((len + pos[0]) & 3);
  1648. zip_write_short(abuf, 0xffff);
  1649. zip_write_short(abuf + 2, align - 4);
  1650. zip_write_int(abuf + 4, 0x03020100);
  1651. if (Tcl_Write(out, abuf, align) != align) {
  1652. goto wrerr;
  1653. }
  1654. }
  1655. if (passwd != NULL) {
  1656. int i, ch, tmp;
  1657. unsigned char kvbuf[24];
  1658. Tcl_Obj *ret;
  1659.  
  1660. init_keys(passwd, keys, crc32tab);
  1661. for (i = 0; i < 12 - 2; i++) {
  1662. if (Tcl_EvalEx(interp, "expr int(rand() * 256) % 256", -1, 0) != TCL_OK) {
  1663. Tcl_AppendResult(interp, "PRNG error", (char *) NULL);
  1664. Tcl_Close(interp, in);
  1665. return TCL_ERROR;
  1666. }
  1667. ret = Tcl_GetObjResult(interp);
  1668. if (Tcl_GetIntFromObj(interp, ret, &ch) != TCL_OK) {
  1669. Tcl_Close(interp, in);
  1670. return TCL_ERROR;
  1671. }
  1672. kvbuf[i + 12] = (unsigned char) zencode(keys, crc32tab, ch, tmp);
  1673. }
  1674. Tcl_ResetResult(interp);
  1675. init_keys(passwd, keys, crc32tab);
  1676. for (i = 0; i < 12 - 2; i++) {
  1677. kvbuf[i] = (unsigned char) zencode(keys, crc32tab,
  1678. kvbuf[i + 12], tmp);
  1679. }
  1680. kvbuf[i++] = (unsigned char) zencode(keys, crc32tab, crc >> 16, tmp);
  1681. kvbuf[i++] = (unsigned char) zencode(keys, crc32tab, crc >> 24, tmp);
  1682. len = Tcl_Write(out, (char *) kvbuf, 12);
  1683. memset(kvbuf, 0, 24);
  1684. if (len != 12) {
  1685. Tcl_AppendResult(interp, "write error", (char *) NULL);
  1686. Tcl_Close(interp, in);
  1687. return TCL_ERROR;
  1688. }
  1689. memcpy(keys0, keys, sizeof (keys0));
  1690. nbytecompr += 12;
  1691. }
  1692. Tcl_Flush(out);
  1693. pos[2] = Tcl_Tell(out);
  1694. cmeth = ZIP_COMPMETH_DEFLATED;
  1695. memset(&stream, 0, sizeof (stream));
  1696. stream.zalloc = Z_NULL;
  1697. stream.zfree = Z_NULL;
  1698. stream.opaque = Z_NULL;
  1699. if (deflateInit2(&stream, 9, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY)
  1700. != Z_OK) {
  1701. Tcl_AppendResult(interp, "compression init error on \"", path, "\"",
  1702. (char *) NULL);
  1703. Tcl_Close(interp, in);
  1704. return TCL_ERROR;
  1705. }
  1706. do {
  1707. len = Tcl_Read(in, buf, bufsize);
  1708. if (len == -1) {
  1709. Tcl_AppendResult(interp, "read error on \"", path, "\"",
  1710. (char *) NULL);
  1711. deflateEnd(&stream);
  1712. Tcl_Close(interp, in);
  1713. return TCL_ERROR;
  1714. }
  1715. stream.avail_in = len;
  1716. stream.next_in = (unsigned char *) buf;
  1717. flush = Tcl_Eof(in) ? Z_FINISH : Z_NO_FLUSH;
  1718. do {
  1719. stream.avail_out = sizeof (obuf);
  1720. stream.next_out = (unsigned char *) obuf;
  1721. len = deflate(&stream, flush);
  1722. if (len == Z_STREAM_ERROR) {
  1723. Tcl_AppendResult(interp, "deflate error on \"", path, "\"",
  1724. (char *) NULL);
  1725. deflateEnd(&stream);
  1726. Tcl_Close(interp, in);
  1727. return TCL_ERROR;
  1728. }
  1729. olen = sizeof (obuf) - stream.avail_out;
  1730. if (passwd != NULL) {
  1731. int i, tmp;
  1732.  
  1733. for (i = 0; i < olen; i++) {
  1734. obuf[i] = (char) zencode(keys, crc32tab, obuf[i], tmp);
  1735. }
  1736. }
  1737. if (olen && (Tcl_Write(out, obuf, olen) != olen)) {
  1738. Tcl_AppendResult(interp, "write error", (char *) NULL);
  1739. deflateEnd(&stream);
  1740. Tcl_Close(interp, in);
  1741. return TCL_ERROR;
  1742. }
  1743. nbytecompr += olen;
  1744. } while (stream.avail_out == 0);
  1745. } while (flush != Z_FINISH);
  1746. deflateEnd(&stream);
  1747. Tcl_Flush(out);
  1748. pos[1] = Tcl_Tell(out);
  1749. if (nbyte - nbytecompr <= 0) {
  1750. /*
  1751. * Compressed file larger than input,
  1752. * write it again uncompressed.
  1753. */
  1754. if ((int) Tcl_Seek(in, 0, SEEK_SET) != 0) {
  1755. goto seekErr;
  1756. }
  1757. if ((int) Tcl_Seek(out, pos[2], SEEK_SET) != pos[2]) {
  1758. seekErr:
  1759. Tcl_Close(interp, in);
  1760. Tcl_AppendResult(interp, "seek error", (char *) NULL);
  1761. return TCL_ERROR;
  1762. }
  1763. nbytecompr = (passwd != NULL) ? 12 : 0;
  1764. while (1) {
  1765. len = Tcl_Read(in, buf, bufsize);
  1766. if (len == -1) {
  1767. Tcl_AppendResult(interp, "read error on \"", path, "\"",
  1768. (char *) NULL);
  1769. Tcl_Close(interp, in);
  1770. return TCL_ERROR;
  1771. } else if (len == 0) {
  1772. break;
  1773. }
  1774. if (passwd != NULL) {
  1775. int i, tmp;
  1776.  
  1777. for (i = 0; i < len; i++) {
  1778. buf[i] = (char) zencode(keys0, crc32tab, buf[i], tmp);
  1779. }
  1780. }
  1781. if (Tcl_Write(out, buf, len) != len) {
  1782. Tcl_AppendResult(interp, "write error", (char *) NULL);
  1783. Tcl_Close(interp, in);
  1784. return TCL_ERROR;
  1785. }
  1786. nbytecompr += len;
  1787. }
  1788. cmeth = ZIP_COMPMETH_STORED;
  1789. Tcl_Flush(out);
  1790. pos[1] = Tcl_Tell(out);
  1791. Tcl_TruncateChannel(out, pos[1]);
  1792. }
  1793. Tcl_Close(interp, in);
  1794.  
  1795. z = (ZipEntry *) Tcl_Alloc(sizeof (*z));
  1796. z->name = NULL;
  1797. z->tnext = NULL;
  1798. z->depth = 0;
  1799. z->zipfile = NULL;
  1800. z->isdir = 0;
  1801. z->isenc = (passwd != NULL) ? 1 : 0;
  1802. z->offset = pos[0];
  1803. z->crc32 = crc;
  1804. z->timestamp = mtime;
  1805. z->nbyte = nbyte;
  1806. z->nbytecompr = nbytecompr;
  1807. z->cmeth = cmeth;
  1808. z->data = NULL;
  1809. hPtr = Tcl_CreateHashEntry(fileHash, zpath, &isNew);
  1810. if (!isNew) {
  1811. Tcl_AppendResult(interp, "non-unique path name \"", path, "\"",
  1812. (char *) NULL);
  1813. Tcl_Free((char *) z);
  1814. return TCL_ERROR;
  1815. } else {
  1816. Tcl_SetHashValue(hPtr, (ClientData) z);
  1817. z->name = Tcl_GetHashKey(fileHash, hPtr);
  1818. z->next = NULL;
  1819. }
  1820.  
  1821. /*
  1822. * Write final local header information.
  1823. */
  1824. zip_write_int(buf + ZIP_LOCAL_SIG_OFFS, ZIP_LOCAL_HEADER_SIG);
  1825. zip_write_short(buf + ZIP_LOCAL_VERSION_OFFS, ZIP_MIN_VERSION);
  1826. zip_write_short(buf + ZIP_LOCAL_FLAGS_OFFS, z->isenc);
  1827. zip_write_short(buf + ZIP_LOCAL_COMPMETH_OFFS, z->cmeth);
  1828. zip_write_short(buf + ZIP_LOCAL_MTIME_OFFS, ToDosTime(z->timestamp));
  1829. zip_write_short(buf + ZIP_LOCAL_MDATE_OFFS, ToDosDate(z->timestamp));
  1830. zip_write_int(buf + ZIP_LOCAL_CRC32_OFFS, z->crc32);
  1831. zip_write_int(buf + ZIP_LOCAL_COMPLEN_OFFS, z->nbytecompr);
  1832. zip_write_int(buf + ZIP_LOCAL_UNCOMPLEN_OFFS, z->nbyte);
  1833. zip_write_short(buf + ZIP_LOCAL_PATHLEN_OFFS, zpathlen);
  1834. zip_write_short(buf + ZIP_LOCAL_EXTRALEN_OFFS, align);
  1835. if ((int) Tcl_Seek(out, pos[0], SEEK_SET) != pos[0]) {
  1836. Tcl_DeleteHashEntry(hPtr);
  1837. Tcl_Free((char *) z);
  1838. Tcl_AppendResult(interp, "seek error", (char *) NULL);
  1839. return TCL_ERROR;
  1840. }
  1841. if (Tcl_Write(out, buf, ZIP_LOCAL_HEADER_LEN) != ZIP_LOCAL_HEADER_LEN) {
  1842. Tcl_DeleteHashEntry(hPtr);
  1843. Tcl_Free((char *) z);
  1844. Tcl_AppendResult(interp, "write error", (char *) NULL);
  1845. return TCL_ERROR;
  1846. }
  1847. Tcl_Flush(out);
  1848. if ((int) Tcl_Seek(out, pos[1], SEEK_SET) != pos[1]) {
  1849. Tcl_DeleteHashEntry(hPtr);
  1850. Tcl_Free((char *) z);
  1851. Tcl_AppendResult(interp, "seek error", (char *) NULL);
  1852. return TCL_ERROR;
  1853. }
  1854. return TCL_OK;
  1855. }
  1856.  
  1857. /*
  1858. *-------------------------------------------------------------------------
  1859. *
  1860. * ZipFSMkZipOrImgObjCmd --
  1861. *
  1862. * This procedure is creates a new ZIP archive file or image file
  1863. * given output filename, input directory of files to be archived,
  1864. * optional password, and optional image to be prepended to the
  1865. * output ZIP archive file.
  1866. *
  1867. * Results:
  1868. * A standard Tcl result.
  1869. *
  1870. * Side effects:
  1871. * A new ZIP archive file or image file is written.
  1872. *
  1873. *-------------------------------------------------------------------------
  1874. */
  1875.  
  1876. static int
  1877. ZipFSMkZipOrImgObjCmd(ClientData clientData, Tcl_Interp *interp,
  1878. int isImg, int isList, int objc, Tcl_Obj *const objv[])
  1879. {
  1880. Tcl_Channel out;
  1881. int len = 0, pwlen = 0, slen = 0, i, count, ret = TCL_ERROR, lobjc, pos[3];
  1882. Tcl_Obj **lobjv, *list = NULL;
  1883. ZipEntry *z;
  1884. Tcl_HashEntry *hPtr;
  1885. Tcl_HashSearch search;
  1886. Tcl_HashTable fileHash;
  1887. char *strip = NULL, *pw = NULL, pwbuf[264], buf[4096];
  1888.  
  1889. if (isList) {
  1890. if ((objc < 3) || (objc > (isImg ? 5 : 4))) {
  1891. Tcl_WrongNumArgs(interp, 1, objv, isImg ?
  1892. "outfile inlist ?password infile?" :
  1893. "outfile inlist ?password?");
  1894. return TCL_ERROR;
  1895. }
  1896. } else {
  1897. if ((objc < 3) || (objc > (isImg ? 6 : 5))) {
  1898. Tcl_WrongNumArgs(interp, 1, objv, isImg ?
  1899. "outfile indir ?strip? ?password? ?infile?" :
  1900. "outfile indir ?strip? ?password?");
  1901. return TCL_ERROR;
  1902. }
  1903. }
  1904. pwbuf[0] = 0;
  1905. if (objc > (isList ? 3 : 4)) {
  1906. pw = Tcl_GetString(objv[isList ? 3 : 4]);
  1907. pwlen = strlen(pw);
  1908. if ((pwlen > 255) || (strchr(pw, 0xff) != NULL)) {
  1909. Tcl_SetObjResult(interp,
  1910. Tcl_NewStringObj("illegal password", -1));
  1911. return TCL_ERROR;
  1912. }
  1913. }
  1914. if (isList) {
  1915. list = objv[2];
  1916. Tcl_IncrRefCount(list);
  1917. } else {
  1918. Tcl_Obj *cmd[3];
  1919.  
  1920. cmd[1] = Tcl_NewStringObj("::zipfs::find", -1);
  1921. cmd[2] = objv[2];
  1922. cmd[0] = Tcl_NewListObj(2, cmd + 1);
  1923. Tcl_IncrRefCount(cmd[0]);
  1924. if (Tcl_EvalObjEx(interp, cmd[0], TCL_EVAL_DIRECT) != TCL_OK) {
  1925. Tcl_DecrRefCount(cmd[0]);
  1926. return TCL_ERROR;
  1927. }
  1928. Tcl_DecrRefCount(cmd[0]);
  1929. list = Tcl_GetObjResult(interp);
  1930. Tcl_IncrRefCount(list);
  1931. }
  1932. if (Tcl_ListObjGetElements(interp, list, &lobjc, &lobjv) != TCL_OK) {
  1933. Tcl_DecrRefCount(list);
  1934. return TCL_ERROR;
  1935. }
  1936. if (isList && (lobjc % 2)) {
  1937. Tcl_DecrRefCount(list);
  1938. Tcl_SetObjResult(interp,
  1939. Tcl_NewStringObj("need even number of elements", -1));
  1940. return TCL_ERROR;
  1941. }
  1942. if (lobjc == 0) {
  1943. Tcl_DecrRefCount(list);
  1944. Tcl_SetObjResult(interp, Tcl_NewStringObj("empty archive", -1));
  1945. return TCL_ERROR;
  1946. }
  1947. out = Tcl_OpenFileChannel(interp, Tcl_GetString(objv[1]), "w", 0755);
  1948. if ((out == NULL) ||
  1949. (Tcl_SetChannelOption(interp, out, "-translation", "binary")
  1950. != TCL_OK) ||
  1951. (Tcl_SetChannelOption(interp, out, "-encoding", "binary")
  1952. != TCL_OK)) {
  1953. Tcl_DecrRefCount(list);
  1954. Tcl_Close(interp, out);
  1955. return TCL_ERROR;
  1956. }
  1957. if (isImg) {
  1958. ZipFile zf0;
  1959. const char *imgName;
  1960.  
  1961. if (isList) {
  1962. imgName = (objc > 4) ? Tcl_GetString(objv[4]) :
  1963. Tcl_GetNameOfExecutable();
  1964. } else {
  1965. imgName = (objc > 5) ? Tcl_GetString(objv[5]) :
  1966. Tcl_GetNameOfExecutable();
  1967. }
  1968. if (ZipFSOpenArchive(interp, imgName, 0, &zf0) != TCL_OK) {
  1969. Tcl_DecrRefCount(list);
  1970. Tcl_Close(interp, out);
  1971. return TCL_ERROR;
  1972. }
  1973. if ((pw != NULL) && pwlen) {
  1974. i = 0;
  1975. len = pwlen;
  1976. while (len > 0) {
  1977. int ch = pw[len - 1];
  1978.  
  1979. pwbuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
  1980. i++;
  1981. len--;
  1982. }
  1983. pwbuf[i] = i;
  1984. ++i;
  1985. pwbuf[i++] = (char) ZIP_PASSWORD_END_SIG;
  1986. pwbuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 8);
  1987. pwbuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 16);
  1988. pwbuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 24);
  1989. pwbuf[i] = '\0';
  1990. }
  1991. i = Tcl_Write(out, (char *) zf0.data, zf0.baseoffsp);
  1992. if (i != zf0.baseoffsp) {
  1993. Tcl_DecrRefCount(list);
  1994. Tcl_SetObjResult(interp, Tcl_NewStringObj("write error", -1));
  1995. Tcl_Close(interp, out);
  1996. ZipFSCloseArchive(interp, &zf0);
  1997. return TCL_ERROR;
  1998. }
  1999. ZipFSCloseArchive(interp, &zf0);
  2000. len = strlen(pwbuf);
  2001. if (len > 0) {
  2002. i = Tcl_Write(out, pwbuf, len);
  2003. if (i != len) {
  2004. Tcl_DecrRefCount(list);
  2005. Tcl_SetObjResult(interp, Tcl_NewStringObj("write error", -1));
  2006. Tcl_Close(interp, out);
  2007. return TCL_ERROR;
  2008. }
  2009. }
  2010. memset(pwbuf, 0, sizeof (pwbuf));
  2011. Tcl_Flush(out);
  2012. }
  2013. Tcl_InitHashTable(&fileHash, TCL_STRING_KEYS);
  2014. pos[0] = Tcl_Tell(out);
  2015. if (!isList && (objc > 3)) {
  2016. strip = Tcl_GetString(objv[3]);
  2017. slen = strlen(strip);
  2018. }
  2019. for (i = 0; i < lobjc; i += (isList ? 2 : 1)) {
  2020. const char *path, *name;
  2021.  
  2022. path = Tcl_GetString(lobjv[i]);
  2023. if (isList) {
  2024. name = Tcl_GetString(lobjv[i + 1]);
  2025. } else {
  2026. name = path;
  2027. if (slen > 0) {
  2028. len = strlen(name);
  2029. if ((len <= slen) || (strncmp(strip, name, slen) != 0)) {
  2030. continue;
  2031. }
  2032. name += slen;
  2033. }
  2034. }
  2035. while (name[0] == '/') {
  2036. ++name;
  2037. }
  2038. if (name[0] == '\0') {
  2039. continue;
  2040. }
  2041. if (ZipAddFile(interp, path, name, out, pw, buf, sizeof (buf),
  2042. &fileHash) != TCL_OK) {
  2043. goto done;
  2044. }
  2045. }
  2046. pos[1] = Tcl_Tell(out);
  2047. count = 0;
  2048. for (i = 0; i < lobjc; i += (isList ? 2 : 1)) {
  2049. const char *path, *name;
  2050.  
  2051. path = Tcl_GetString(lobjv[i]);
  2052. if (isList) {
  2053. name = Tcl_GetString(lobjv[i + 1]);
  2054. } else {
  2055. name = path;
  2056. if (slen > 0) {
  2057. len = strlen(name);
  2058. if ((len <= slen) || (strncmp(strip, name, slen) != 0)) {
  2059. continue;
  2060. }
  2061. name += slen;
  2062. }
  2063. }
  2064. while (name[0] == '/') {
  2065. ++name;
  2066. }
  2067. if (name[0] == '\0') {
  2068. continue;
  2069. }
  2070. hPtr = Tcl_FindHashEntry(&fileHash, name);
  2071. if (hPtr == NULL) {
  2072. continue;
  2073. }
  2074. z = (ZipEntry *) Tcl_GetHashValue(hPtr);
  2075. len = strlen(z->name);
  2076. zip_write_int(buf + ZIP_CENTRAL_SIG_OFFS, ZIP_CENTRAL_HEADER_SIG);
  2077. zip_write_short(buf + ZIP_CENTRAL_VERSIONMADE_OFFS, ZIP_MIN_VERSION);
  2078. zip_write_short(buf + ZIP_CENTRAL_VERSION_OFFS, ZIP_MIN_VERSION);
  2079. zip_write_short(buf + ZIP_CENTRAL_FLAGS_OFFS, z->isenc ? 1 : 0);
  2080. zip_write_short(buf + ZIP_CENTRAL_COMPMETH_OFFS, z->cmeth);
  2081. zip_write_short(buf + ZIP_CENTRAL_MTIME_OFFS, ToDosTime(z->timestamp));
  2082. zip_write_short(buf + ZIP_CENTRAL_MDATE_OFFS, ToDosDate(z->timestamp));
  2083. zip_write_int(buf + ZIP_CENTRAL_CRC32_OFFS, z->crc32);
  2084. zip_write_int(buf + ZIP_CENTRAL_COMPLEN_OFFS, z->nbytecompr);
  2085. zip_write_int(buf + ZIP_CENTRAL_UNCOMPLEN_OFFS, z->nbyte);
  2086. zip_write_short(buf + ZIP_CENTRAL_PATHLEN_OFFS, len);
  2087. zip_write_short(buf + ZIP_CENTRAL_EXTRALEN_OFFS, 0);
  2088. zip_write_short(buf + ZIP_CENTRAL_FCOMMENTLEN_OFFS, 0);
  2089. zip_write_short(buf + ZIP_CENTRAL_DISKFILE_OFFS, 0);
  2090. zip_write_short(buf + ZIP_CENTRAL_IATTR_OFFS, 0);
  2091. zip_write_int(buf + ZIP_CENTRAL_EATTR_OFFS, 0);
  2092. zip_write_int(buf + ZIP_CENTRAL_LOCALHDR_OFFS, z->offset - pos[0]);
  2093. if ((Tcl_Write(out, buf, ZIP_CENTRAL_HEADER_LEN) !=
  2094. ZIP_CENTRAL_HEADER_LEN) ||
  2095. (Tcl_Write(out, z->name, len) != len)) {
  2096. Tcl_SetObjResult(interp, Tcl_NewStringObj("write error", -1));
  2097. goto done;
  2098. }
  2099. count++;
  2100. }
  2101. Tcl_Flush(out);
  2102. pos[2] = Tcl_Tell(out);
  2103. zip_write_int(buf + ZIP_CENTRAL_END_SIG_OFFS, ZIP_CENTRAL_END_SIG);
  2104. zip_write_short(buf + ZIP_CENTRAL_DISKNO_OFFS, 0);
  2105. zip_write_short(buf + ZIP_CENTRAL_DISKDIR_OFFS, 0);
  2106. zip_write_short(buf + ZIP_CENTRAL_ENTS_OFFS, count);
  2107. zip_write_short(buf + ZIP_CENTRAL_TOTALENTS_OFFS, count);
  2108. zip_write_int(buf + ZIP_CENTRAL_DIRSIZE_OFFS, pos[2] - pos[1]);
  2109. zip_write_int(buf + ZIP_CENTRAL_DIRSTART_OFFS, pos[1] - pos[0]);
  2110. zip_write_short(buf + ZIP_CENTRAL_COMMENTLEN_OFFS, 0);
  2111. if (Tcl_Write(out, buf, ZIP_CENTRAL_END_LEN) != ZIP_CENTRAL_END_LEN) {
  2112. Tcl_SetObjResult(interp, Tcl_NewStringObj("write error", -1));
  2113. goto done;
  2114. }
  2115. Tcl_Flush(out);
  2116. ret = TCL_OK;
  2117. done:
  2118. if (ret == TCL_OK) {
  2119. ret = Tcl_Close(interp, out);
  2120. } else {
  2121. Tcl_Close(interp, out);
  2122. }
  2123. Tcl_DecrRefCount(list);
  2124. hPtr = Tcl_FirstHashEntry(&fileHash, &search);
  2125. while (hPtr != NULL) {
  2126. z = (ZipEntry *) Tcl_GetHashValue(hPtr);
  2127. Tcl_Free((char *) z);
  2128. Tcl_DeleteHashEntry(hPtr);
  2129. hPtr = Tcl_FirstHashEntry(&fileHash, &search);
  2130. }
  2131. Tcl_DeleteHashTable(&fileHash);
  2132. return ret;
  2133. }
  2134.  
  2135. /*
  2136. *-------------------------------------------------------------------------
  2137. *
  2138. * ZipFSMkZipObjCmd --
  2139. *
  2140. * This procedure is invoked to process the "zipfs::mkzip" command.
  2141. * See description of ZipFSMkZipOrImgCmd().
  2142. *
  2143. * Results:
  2144. * A standard Tcl result.
  2145. *
  2146. * Side effects:
  2147. * See description of ZipFSMkZipOrImgCmd().
  2148. *
  2149. *-------------------------------------------------------------------------
  2150. */
  2151.  
  2152. static int
  2153. ZipFSMkZipObjCmd(ClientData clientData, Tcl_Interp *interp,
  2154. int objc, Tcl_Obj *const objv[])
  2155. {
  2156. return ZipFSMkZipOrImgObjCmd(clientData, interp, 0, 0, objc, objv);
  2157. }
  2158.  
  2159. static int
  2160. ZipFSLMkZipObjCmd(ClientData clientData, Tcl_Interp *interp,
  2161. int objc, Tcl_Obj *const objv[])
  2162. {
  2163. return ZipFSMkZipOrImgObjCmd(clientData, interp, 0, 1, objc, objv);
  2164. }
  2165.  
  2166. /*
  2167. *-------------------------------------------------------------------------
  2168. *
  2169. * ZipFSMkImgObjCmd --
  2170. *
  2171. * This procedure is invoked to process the "zipfs::mkimg" command.
  2172. * See description of ZipFSMkZipOrImgCmd().
  2173. *
  2174. * Results:
  2175. * A standard Tcl result.
  2176. *
  2177. * Side effects:
  2178. * See description of ZipFSMkZipOrImgCmd().
  2179. *
  2180. *-------------------------------------------------------------------------
  2181. */
  2182.  
  2183. static int
  2184. ZipFSMkImgObjCmd(ClientData clientData, Tcl_Interp *interp,
  2185. int objc, Tcl_Obj *const objv[])
  2186. {
  2187. return ZipFSMkZipOrImgObjCmd(clientData, interp, 1, 0, objc, objv);
  2188. }
  2189.  
  2190. static int
  2191. ZipFSLMkImgObjCmd(ClientData clientData, Tcl_Interp *interp,
  2192. int objc, Tcl_Obj *const objv[])
  2193. {
  2194. return ZipFSMkZipOrImgObjCmd(clientData, interp, 1, 1, objc, objv);
  2195. }
  2196.  
  2197. /*
  2198. *-------------------------------------------------------------------------
  2199. *
  2200. * ZipFSExistsObjCmd --
  2201. *
  2202. * This procedure is invoked to process the "zipfs::exists" command.
  2203. * It tests for the existence of a file in the ZIP filesystem and
  2204. * places a boolean into the interp's result.
  2205. *
  2206. * Results:
  2207. * Always TCL_OK.
  2208. *
  2209. * Side effects:
  2210. * None.
  2211. *
  2212. *-------------------------------------------------------------------------
  2213. */
  2214.  
  2215. static int
  2216. ZipFSCanonicalObjCmd(ClientData clientData, Tcl_Interp *interp,
  2217. int objc, Tcl_Obj *const objv[])
  2218. {
  2219. char *mntpoint=NULL;
  2220. char *filename=NULL;
  2221. char *result;
  2222. Tcl_DString dPath;
  2223.  
  2224. if (objc != 2 && objc != 3 && objc!=4) {
  2225. Tcl_WrongNumArgs(interp, 1, objv, "?mntpnt? filename ?ZIPFS?");
  2226. return TCL_ERROR;
  2227. }
  2228. Tcl_DStringInit(&dPath);
  2229. if(objc==2) {
  2230. filename = Tcl_GetString(objv[1]);
  2231. result=CanonicalPath("",filename,&dPath,1);
  2232. } else if (objc==3) {
  2233. mntpoint = Tcl_GetString(objv[1]);
  2234. filename = Tcl_GetString(objv[2]);
  2235. result=CanonicalPath(mntpoint,filename,&dPath,1);
  2236. } else {
  2237. int zipfs=0;
  2238. if(Tcl_GetBooleanFromObj(interp,objv[3],&zipfs)) {
  2239. return TCL_ERROR;
  2240. }
  2241. mntpoint = Tcl_GetString(objv[1]);
  2242. filename = Tcl_GetString(objv[2]);
  2243. result=CanonicalPath(mntpoint,filename,&dPath,zipfs);
  2244. }
  2245. Tcl_SetObjResult(interp,Tcl_NewStringObj(result,-1));
  2246. return TCL_OK;
  2247. }
  2248.  
  2249. /*
  2250. *-------------------------------------------------------------------------
  2251. *
  2252. * ZipFSExistsObjCmd --
  2253. *
  2254. * This procedure is invoked to process the "zipfs::exists" command.
  2255. * It tests for the existence of a file in the ZIP filesystem and
  2256. * places a boolean into the interp's result.
  2257. *
  2258. * Results:
  2259. * Always TCL_OK.
  2260. *
  2261. * Side effects:
  2262. * None.
  2263. *
  2264. *-------------------------------------------------------------------------
  2265. */
  2266.  
  2267. static int
  2268. ZipFSExistsObjCmd(ClientData clientData, Tcl_Interp *interp,
  2269. int objc, Tcl_Obj *const objv[])
  2270. {
  2271. char *filename;
  2272. int exists;
  2273. Tcl_DString ds;
  2274.  
  2275. if (objc != 2) {
  2276. Tcl_WrongNumArgs(interp, 1, objv, "filename");
  2277. return TCL_ERROR;
  2278. }
  2279.  
  2280. /* prepend ZIPFS_VOLUME to filename, eliding the final / */
  2281. filename = Tcl_GetStringFromObj(objv[1], 0);
  2282. Tcl_DStringInit(&ds);
  2283. Tcl_DStringAppend(&ds, ZIPFS_VOLUME, ZIPFS_VOLUME_LEN-1);
  2284. Tcl_DStringAppend(&ds, filename, -1);
  2285. filename = Tcl_DStringValue(&ds);
  2286.  
  2287. ReadLock();
  2288. exists = ZipFSLookup(filename) != NULL;
  2289. Unlock();
  2290.  
  2291. Tcl_SetObjResult(interp,Tcl_NewBooleanObj(exists));
  2292. return TCL_OK;
  2293. }
  2294.  
  2295. /*
  2296. *-------------------------------------------------------------------------
  2297. *
  2298. * ZipFSInfoObjCmd --
  2299. *
  2300. * This procedure is invoked to process the "zipfs::info" command.
  2301. * On success, it returns a Tcl list made up of name of ZIP archive
  2302. * file, size uncompressed, size compressed, and archive offset of
  2303. * a file in the ZIP filesystem.
  2304. *
  2305. * Results:
  2306. * A standard Tcl result.
  2307. *
  2308. * Side effects:
  2309. * None.
  2310. *
  2311. *-------------------------------------------------------------------------
  2312. */
  2313.  
  2314. static int
  2315. ZipFSInfoObjCmd(ClientData clientData, Tcl_Interp *interp,
  2316. int objc, Tcl_Obj *const objv[])
  2317. {
  2318. char *filename;
  2319. ZipEntry *z;
  2320.  
  2321. if (objc != 2) {
  2322. Tcl_WrongNumArgs(interp, 1, objv, "filename");
  2323. return TCL_ERROR;
  2324. }
  2325. filename = Tcl_GetStringFromObj(objv[1], 0);
  2326. ReadLock();
  2327. z = ZipFSLookup(filename);
  2328. if (z != NULL) {
  2329. Tcl_Obj *result = Tcl_GetObjResult(interp);
  2330.  
  2331. Tcl_ListObjAppendElement(interp, result,
  2332. Tcl_NewStringObj(z->zipfile->name, -1));
  2333. Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(z->nbyte));
  2334. Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(z->nbytecompr));
  2335. Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(z->offset));
  2336. }
  2337. Unlock();
  2338. return TCL_OK;
  2339. }
  2340.  
  2341. /*
  2342. *-------------------------------------------------------------------------
  2343. *
  2344. * ZipFSListObjCmd --
  2345. *
  2346. * This procedure is invoked to process the "zipfs::list" command.
  2347. * On success, it returns a Tcl list of files of the ZIP filesystem
  2348. * which match a search pattern (glob or regexp).
  2349. *
  2350. * Results:
  2351. * A standard Tcl result.
  2352. *
  2353. * Side effects:
  2354. * None.
  2355. *
  2356. *-------------------------------------------------------------------------
  2357. */
  2358.  
  2359. static int
  2360. ZipFSListObjCmd(ClientData clientData, Tcl_Interp *interp,
  2361. int objc, Tcl_Obj *const objv[])
  2362. {
  2363. char *pattern = NULL;
  2364. Tcl_RegExp regexp = NULL;
  2365. Tcl_HashEntry *hPtr;
  2366. Tcl_HashSearch search;
  2367. Tcl_Obj *result = Tcl_GetObjResult(interp);
  2368.  
  2369. if (objc > 3) {
  2370. Tcl_WrongNumArgs(interp, 1, objv, "?(-glob|-regexp)? ?pattern?");
  2371. return TCL_ERROR;
  2372. }
  2373. if (objc == 3) {
  2374. int n;
  2375. char *what = Tcl_GetStringFromObj(objv[1], &n);
  2376.  
  2377. if ((n >= 2) && (strncmp(what, "-glob", n) == 0)) {
  2378. pattern = Tcl_GetString(objv[2]);
  2379. } else if ((n >= 2) && (strncmp(what, "-regexp", n) == 0)) {
  2380. regexp = Tcl_RegExpCompile(interp, Tcl_GetString(objv[2]));
  2381. if (regexp == NULL) {
  2382. return TCL_ERROR;
  2383. }
  2384. } else {
  2385. Tcl_AppendResult(interp, "unknown option \"", what,
  2386. "\"", (char *) NULL);
  2387. return TCL_ERROR;
  2388. }
  2389. } else if (objc == 2) {
  2390. pattern = Tcl_GetStringFromObj(objv[1], 0);
  2391. }
  2392. ReadLock();
  2393. if (pattern != NULL) {
  2394. for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
  2395. hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  2396. ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
  2397.  
  2398. if (Tcl_StringMatch(z->name, pattern)) {
  2399. Tcl_ListObjAppendElement(interp, result,
  2400. Tcl_NewStringObj(z->name, -1));
  2401. }
  2402. }
  2403. } else if (regexp != NULL) {
  2404. for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
  2405. hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  2406. ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
  2407.  
  2408. if (Tcl_RegExpExec(interp, regexp, z->name, z->name)) {
  2409. Tcl_ListObjAppendElement(interp, result,
  2410. Tcl_NewStringObj(z->name, -1));
  2411. }
  2412. }
  2413. } else {
  2414. for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
  2415. hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  2416. ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
  2417.  
  2418. Tcl_ListObjAppendElement(interp, result,
  2419. Tcl_NewStringObj(z->name, -1));
  2420. }
  2421. }
  2422. Unlock();
  2423. return TCL_OK;
  2424. }
  2425.  
  2426. /*
  2427. *-------------------------------------------------------------------------
  2428. *
  2429. * ZipChannelClose --
  2430. *
  2431. * This function is called to close a channel.
  2432. *
  2433. * Results:
  2434. * Always TCL_OK.
  2435. *
  2436. * Side effects:
  2437. * Resources are free'd.
  2438. *
  2439. *-------------------------------------------------------------------------
  2440. */
  2441.  
  2442. static int
  2443. ZipChannelClose(ClientData instanceData, Tcl_Interp *interp)
  2444. {
  2445. ZipChannel *info = (ZipChannel *) instanceData;
  2446.  
  2447. if (info->iscompr && (info->ubuf != NULL)) {
  2448. Tcl_Free((char *) info->ubuf);
  2449. info->ubuf = NULL;
  2450. }
  2451. if (info->isenc) {
  2452. info->isenc = 0;
  2453. memset(info->keys, 0, sizeof (info->keys));
  2454. }
  2455. if (info->iswr) {
  2456. ZipEntry *z = info->zipentry;
  2457. unsigned char *newdata;
  2458.  
  2459. newdata = (unsigned char *)
  2460. Tcl_AttemptRealloc((char *) info->ubuf, info->nread);
  2461. if (newdata != NULL) {
  2462. if (z->data != NULL) {
  2463. Tcl_Free((char *) z->data);
  2464. }
  2465. z->data = newdata;
  2466. z->nbyte = z->nbytecompr = info->nbyte;
  2467. z->cmeth = ZIP_COMPMETH_STORED;
  2468. z->timestamp = time(NULL);
  2469. z->isdir = 0;
  2470. z->isenc = 0;
  2471. z->offset = 0;
  2472. z->crc32 = 0;
  2473. } else {
  2474. Tcl_Free((char *) info->ubuf);
  2475. }
  2476. }
  2477. WriteLock();
  2478. info->zipfile->nopen--;
  2479. Unlock();
  2480. Tcl_Free((char *) info);
  2481. return TCL_OK;
  2482. }
  2483.  
  2484. /*
  2485. *-------------------------------------------------------------------------
  2486. *
  2487. * ZipChannelRead --
  2488. *
  2489. * This function is called to read data from channel.
  2490. *
  2491. * Results:
  2492. * Number of bytes read or -1 on error with error number set.
  2493. *
  2494. * Side effects:
  2495. * Data is read and file pointer is advanced.
  2496. *
  2497. *-------------------------------------------------------------------------
  2498. */
  2499.  
  2500. static int
  2501. ZipChannelRead(ClientData instanceData, char *buf, int toRead, int *errloc)
  2502. {
  2503. ZipChannel *info = (ZipChannel *) instanceData;
  2504. unsigned long nextpos;
  2505.  
  2506. if (info->isdir) {
  2507. *errloc = EISDIR;
  2508. return -1;
  2509. }
  2510. nextpos = info->nread + toRead;
  2511. if (nextpos > info->nbyte) {
  2512. toRead = info->nbyte - info->nread;
  2513. nextpos = info->nbyte;
  2514. }
  2515. if (toRead == 0) {
  2516. return 0;
  2517. }
  2518. if (info->isenc) {
  2519. int i, ch;
  2520.  
  2521. for (i = 0; i < toRead; i++) {
  2522. ch = info->ubuf[i + info->nread];
  2523. buf[i] = zdecode(info->keys, crc32tab, ch);
  2524. }
  2525. } else {
  2526. memcpy(buf, info->ubuf + info->nread, toRead);
  2527. }
  2528. info->nread = nextpos;
  2529. *errloc = 0;
  2530. return toRead;
  2531. }
  2532.  
  2533. /*
  2534. *-------------------------------------------------------------------------
  2535. *
  2536. * ZipChannelWrite --
  2537. *
  2538. * This function is called to write data into channel.
  2539. *
  2540. * Results:
  2541. * Number of bytes written or -1 on error with error number set.
  2542. *
  2543. * Side effects:
  2544. * Data is written and file pointer is advanced.
  2545. *
  2546. *-------------------------------------------------------------------------
  2547. */
  2548.  
  2549. static int
  2550. ZipChannelWrite(ClientData instanceData, const char *buf,
  2551. int toWrite, int *errloc)
  2552. {
  2553. ZipChannel *info = (ZipChannel *) instanceData;
  2554. unsigned long nextpos;
  2555.  
  2556. if (!info->iswr) {
  2557. *errloc = EINVAL;
  2558. return -1;
  2559. }
  2560. nextpos = info->nread + toWrite;
  2561. if (nextpos > info->nmax) {
  2562. toWrite = info->nmax - info->nread;
  2563. nextpos = info->nmax;
  2564. }
  2565. if (toWrite == 0) {
  2566. return 0;
  2567. }
  2568. memcpy(info->ubuf + info->nread, buf, toWrite);
  2569. info->nread = nextpos;
  2570. if (info->nread > info->nbyte) {
  2571. info->nbyte = info->nread;
  2572. }
  2573. *errloc = 0;
  2574. return toWrite;
  2575. }
  2576.  
  2577. /*
  2578. *-------------------------------------------------------------------------
  2579. *
  2580. * ZipChannelSeek --
  2581. *
  2582. * This function is called to position file pointer of channel.
  2583. *
  2584. * Results:
  2585. * New file position or -1 on error with error number set.
  2586. *
  2587. * Side effects:
  2588. * File pointer is repositioned according to offset and mode.
  2589. *
  2590. *-------------------------------------------------------------------------
  2591. */
  2592.  
  2593. static int
  2594. ZipChannelSeek(ClientData instanceData, long offset, int mode, int *errloc)
  2595. {
  2596. ZipChannel *info = (ZipChannel *) instanceData;
  2597.  
  2598. if (info->isdir) {
  2599. *errloc = EINVAL;
  2600. return -1;
  2601. }
  2602. switch (mode) {
  2603. case SEEK_CUR:
  2604. offset += info->nread;
  2605. break;
  2606. case SEEK_END:
  2607. offset += info->nbyte;
  2608. break;
  2609. case SEEK_SET:
  2610. break;
  2611. default:
  2612. *errloc = EINVAL;
  2613. return -1;
  2614. }
  2615. if (offset < 0) {
  2616. *errloc = EINVAL;
  2617. return -1;
  2618. }
  2619. if (info->iswr) {
  2620. if ((unsigned long) offset > info->nmax) {
  2621. *errloc = EINVAL;
  2622. return -1;
  2623. }
  2624. if ((unsigned long) offset > info->nbyte) {
  2625. info->nbyte = offset;
  2626. }
  2627. } else if ((unsigned long) offset > info->nbyte) {
  2628. *errloc = EINVAL;
  2629. return -1;
  2630. }
  2631. info->nread = (unsigned long) offset;
  2632. return info->nread;
  2633. }
  2634.  
  2635. /*
  2636. *-------------------------------------------------------------------------
  2637. *
  2638. * ZipChannelWatchChannel --
  2639. *
  2640. * This function is called for event notifications on channel.
  2641. *
  2642. * Results:
  2643. * None.
  2644. *
  2645. * Side effects:
  2646. * None.
  2647. *
  2648. *-------------------------------------------------------------------------
  2649. */
  2650.  
  2651. static void
  2652. ZipChannelWatchChannel(ClientData instanceData, int mask)
  2653. {
  2654. return;
  2655. }
  2656.  
  2657. /*
  2658. *-------------------------------------------------------------------------
  2659. *
  2660. * ZipChannelGetFile --
  2661. *
  2662. * This function is called to retrieve OS handle for channel.
  2663. *
  2664. * Results:
  2665. * Always TCL_ERROR since there's never an OS handle for a
  2666. * file within a ZIP archive.
  2667. *
  2668. * Side effects:
  2669. * None.
  2670. *
  2671. *-------------------------------------------------------------------------
  2672. */
  2673.  
  2674. static int
  2675. ZipChannelGetFile(ClientData instanceData, int direction,
  2676. ClientData *handlePtr)
  2677. {
  2678. return TCL_ERROR;
  2679. }
  2680.  
  2681. /*
  2682. * The channel type/driver definition used for ZIP archive members.
  2683. */
  2684.  
  2685. static Tcl_ChannelType ZipChannelType = {
  2686. "zip", /* Type name. */
  2687. #ifdef TCL_CHANNEL_VERSION_4
  2688. TCL_CHANNEL_VERSION_4,
  2689. ZipChannelClose, /* Close channel, clean instance data */
  2690. ZipChannelRead, /* Handle read request */
  2691. ZipChannelWrite, /* Handle write request */
  2692. ZipChannelSeek, /* Move location of access point, NULL'able */
  2693. NULL, /* Set options, NULL'able */
  2694. NULL, /* Get options, NULL'able */
  2695. ZipChannelWatchChannel, /* Initialize notifier */
  2696. ZipChannelGetFile, /* Get OS handle from the channel */
  2697. NULL, /* 2nd version of close channel, NULL'able */
  2698. NULL, /* Set blocking mode for raw channel, NULL'able */
  2699. NULL, /* Function to flush channel, NULL'able */
  2700. NULL, /* Function to handle event, NULL'able */
  2701. NULL, /* Wide seek function, NULL'able */
  2702. NULL, /* Thread action function, NULL'able */
  2703. #else
  2704. NULL, /* Set blocking/nonblocking behaviour, NULL'able */
  2705. ZipChannelClose, /* Close channel, clean instance data */
  2706. ZipChannelRead, /* Handle read request */
  2707. ZipChannelWrite, /* Handle write request */
  2708. ZipChannelSeek, /* Move location of access point, NULL'able */
  2709. NULL, /* Set options, NULL'able */
  2710. NULL, /* Get options, NULL'able */
  2711. ZipChannelWatchChannel, /* Initialize notifier */
  2712. ZipChannelGetFile, /* Get OS handle from the channel */
  2713. #endif
  2714. };
  2715.  
  2716. /*
  2717. *-------------------------------------------------------------------------
  2718. *
  2719. * ZipChannelOpen --
  2720. *
  2721. * This function opens a Tcl_Channel on a file from a mounted ZIP
  2722. * archive according to given open mode.
  2723. *
  2724. * Results:
  2725. * Tcl_Channel on success, or NULL on error.
  2726. *
  2727. * Side effects:
  2728. * Memory is allocated, the file from the ZIP archive is uncompressed.
  2729. *
  2730. *-------------------------------------------------------------------------
  2731. */
  2732.  
  2733. static Tcl_Channel
  2734. ZipChannelOpen(Tcl_Interp *interp, char *filename, int mode, int permissions)
  2735. {
  2736. ZipEntry *z;
  2737. ZipChannel *info;
  2738. int i, ch, trunc, wr, flags = 0;
  2739. char cname[128];
  2740.  
  2741. if ((mode & O_APPEND) ||
  2742. ((ZipFS.wrmax <= 0) && (mode & (O_WRONLY | O_RDWR)))) {
  2743. if (interp != NULL) {
  2744. Tcl_SetObjResult(interp, Tcl_NewStringObj("unsupported open mode", -1));
  2745. }
  2746. return NULL;
  2747. }
  2748. WriteLock();
  2749. z = ZipFSLookup(filename);
  2750. if (z == NULL) {
  2751. if (interp != NULL) {
  2752. Tcl_SetObjResult(interp, Tcl_NewStringObj("file not found", -1));
  2753. Tcl_AppendResult(interp, " \"", filename, "\"", NULL);
  2754. }
  2755. goto error;
  2756. }
  2757. trunc = (mode & O_TRUNC) != 0;
  2758. wr = (mode & (O_WRONLY | O_RDWR)) != 0;
  2759. if ((z->cmeth != ZIP_COMPMETH_STORED) &&
  2760. (z->cmeth != ZIP_COMPMETH_DEFLATED)) {
  2761. if (interp != NULL) {
  2762. Tcl_SetObjResult(interp,
  2763. Tcl_NewStringObj("unsupported compression method", -1));
  2764. }
  2765. goto error;
  2766. }
  2767. if (wr && z->isdir) {
  2768. if (interp != NULL) {
  2769. Tcl_SetObjResult(interp,
  2770. Tcl_NewStringObj("unsupported file type", -1));
  2771. }
  2772. goto error;
  2773. }
  2774. if (!trunc) {
  2775. flags |= TCL_READABLE;
  2776. if (z->isenc && (z->zipfile->pwbuf[0] == 0)) {
  2777. if (interp != NULL) {
  2778. Tcl_SetObjResult(interp,
  2779. Tcl_NewStringObj("decryption failed", -1));
  2780. }
  2781. goto error;
  2782. } else if (wr && (z->data == NULL) && (z->nbyte > ZipFS.wrmax)) {
  2783. if (interp != NULL) {
  2784. Tcl_SetObjResult(interp,
  2785. Tcl_NewStringObj("file too large", -1));
  2786. }
  2787. goto error;
  2788. }
  2789. } else {
  2790. flags = TCL_WRITABLE;
  2791. }
  2792. info = (ZipChannel *) Tcl_AttemptAlloc(sizeof (*info));
  2793. if (info == NULL) {
  2794. if (interp != NULL) {
  2795. Tcl_SetObjResult(interp, Tcl_NewStringObj("out of memory", -1));
  2796. }
  2797. goto error;
  2798. }
  2799. info->zipfile = z->zipfile;
  2800. info->zipentry = z;
  2801. info->nread = 0;
  2802. if (wr) {
  2803. flags |= TCL_WRITABLE;
  2804. info->iswr = 1;
  2805. info->isdir = 0;
  2806. info->nmax = ZipFS.wrmax;
  2807. info->iscompr = 0;
  2808. info->isenc = 0;
  2809. info->ubuf = (unsigned char *) Tcl_AttemptAlloc(info->nmax);
  2810. if (info->ubuf == NULL) {
  2811. merror0:
  2812. if (info->ubuf != NULL) {
  2813. Tcl_Free((char *) info->ubuf);
  2814. }
  2815. Tcl_Free((char *) info);
  2816. if (interp != NULL) {
  2817. Tcl_SetObjResult(interp,
  2818. Tcl_NewStringObj("out of memory", -1));
  2819. }
  2820. goto error;
  2821. }
  2822. memset(info->ubuf, 0, info->nmax);
  2823. if (trunc) {
  2824. info->nbyte = 0;
  2825. } else {
  2826. if (z->data != NULL) {
  2827. unsigned int j = z->nbyte;
  2828.  
  2829. if (j > info->nmax) {
  2830. j = info->nmax;
  2831. }
  2832. memcpy(info->ubuf, z->data, j);
  2833. info->nbyte = j;
  2834. } else {
  2835. unsigned char *zbuf = z->zipfile->data + z->offset;
  2836.  
  2837. if (z->isenc) {
  2838. int len = z->zipfile->pwbuf[0];
  2839. char pwbuf[260];
  2840.  
  2841. for (i = 0; i < len; i++) {
  2842. ch = z->zipfile->pwbuf[len - i];
  2843. pwbuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
  2844. }
  2845. pwbuf[i] = '\0';
  2846. init_keys(pwbuf, info->keys, crc32tab);
  2847. memset(pwbuf, 0, sizeof (pwbuf));
  2848. for (i = 0; i < 12; i++) {
  2849. ch = info->ubuf[i];
  2850. zdecode(info->keys, crc32tab, ch);
  2851. }
  2852. zbuf += i;
  2853. }
  2854. if (z->cmeth == ZIP_COMPMETH_DEFLATED) {
  2855. z_stream stream;
  2856. int err;
  2857. unsigned char *cbuf = NULL;
  2858.  
  2859. memset(&stream, 0, sizeof (stream));
  2860. stream.zalloc = Z_NULL;
  2861. stream.zfree = Z_NULL;
  2862. stream.opaque = Z_NULL;
  2863. stream.avail_in = z->nbytecompr;
  2864. if (z->isenc) {
  2865. unsigned int j;
  2866.  
  2867. stream.avail_in -= 12;
  2868. cbuf = (unsigned char *)
  2869. Tcl_AttemptAlloc(stream.avail_in);
  2870. if (cbuf == NULL) {
  2871. goto merror0;
  2872. }
  2873. for (j = 0; j < stream.avail_in; j++) {
  2874. ch = info->ubuf[j];
  2875. cbuf[j] = zdecode(info->keys, crc32tab, ch);
  2876. }
  2877. stream.next_in = cbuf;
  2878. } else {
  2879. stream.next_in = zbuf;
  2880. }
  2881. stream.next_out = info->ubuf;
  2882. stream.avail_out = info->nmax;
  2883. if (inflateInit2(&stream, -15) != Z_OK) {
  2884. goto cerror0;
  2885. }
  2886. err = inflate(&stream, Z_SYNC_FLUSH);
  2887. inflateEnd(&stream);
  2888. if ((err == Z_STREAM_END) ||
  2889. ((err == Z_OK) && (stream.avail_in == 0))) {
  2890. if (cbuf != NULL) {
  2891. memset(info->keys, 0, sizeof (info->keys));
  2892. Tcl_Free((char *) cbuf);
  2893. }
  2894. goto wrapchan;
  2895. }
  2896. cerror0:
  2897. if (cbuf != NULL) {
  2898. memset(info->keys, 0, sizeof (info->keys));
  2899. Tcl_Free((char *) cbuf);
  2900. }
  2901. if (info->ubuf != NULL) {
  2902. Tcl_Free((char *) info->ubuf);
  2903. }
  2904. Tcl_Free((char *) info);
  2905. if (interp != NULL) {
  2906. Tcl_SetObjResult(interp,
  2907. Tcl_NewStringObj("decompression error", -1));
  2908. }
  2909. goto error;
  2910. } else if (z->isenc) {
  2911. for (i = 0; i < z->nbyte - 12; i++) {
  2912. ch = zbuf[i];
  2913. info->ubuf[i] = zdecode(info->keys, crc32tab, ch);
  2914. }
  2915. } else {
  2916. memcpy(info->ubuf, zbuf, z->nbyte);
  2917. }
  2918. memset(info->keys, 0, sizeof (info->keys));
  2919. goto wrapchan;
  2920. }
  2921. }
  2922. } else if (z->data != NULL) {
  2923. flags |= TCL_READABLE;
  2924. info->iswr = 0;
  2925. info->iscompr = 0;
  2926. info->isdir = 0;
  2927. info->isenc = 0;
  2928. info->nbyte = z->nbyte;
  2929. info->nmax = 0;
  2930. info->ubuf = z->data;
  2931. } else {
  2932. flags |= TCL_READABLE;
  2933. info->iswr = 0;
  2934. info->iscompr = z->cmeth == ZIP_COMPMETH_DEFLATED;
  2935. info->ubuf = z->zipfile->data + z->offset;
  2936. info->isdir = z->isdir;
  2937. info->isenc = z->isenc;
  2938. info->nbyte = z->nbyte;
  2939. info->nmax = 0;
  2940. if (info->isenc) {
  2941. int len = z->zipfile->pwbuf[0];
  2942. char pwbuf[260];
  2943.  
  2944. for (i = 0; i < len; i++) {
  2945. ch = z->zipfile->pwbuf[len - i];
  2946. pwbuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
  2947. }
  2948. pwbuf[i] = '\0';
  2949. init_keys(pwbuf, info->keys, crc32tab);
  2950. memset(pwbuf, 0, sizeof (pwbuf));
  2951. for (i = 0; i < 12; i++) {
  2952. ch = info->ubuf[i];
  2953. zdecode(info->keys, crc32tab, ch);
  2954. }
  2955. info->ubuf += i;
  2956. }
  2957. if (info->iscompr) {
  2958. z_stream stream;
  2959. int err;
  2960. unsigned char *ubuf = NULL;
  2961. unsigned int j;
  2962.  
  2963. memset(&stream, 0, sizeof (stream));
  2964. stream.zalloc = Z_NULL;
  2965. stream.zfree = Z_NULL;
  2966. stream.opaque = Z_NULL;
  2967. stream.avail_in = z->nbytecompr;
  2968. if (info->isenc) {
  2969. stream.avail_in -= 12;
  2970. ubuf = (unsigned char *) Tcl_AttemptAlloc(stream.avail_in);
  2971. if (ubuf == NULL) {
  2972. info->ubuf = NULL;
  2973. goto merror;
  2974. }
  2975. for (j = 0; j < stream.avail_in; j++) {
  2976. ch = info->ubuf[j];
  2977. ubuf[j] = zdecode(info->keys, crc32tab, ch);
  2978. }
  2979. stream.next_in = ubuf;
  2980. } else {
  2981. stream.next_in = info->ubuf;
  2982. }
  2983. stream.next_out = info->ubuf =
  2984. (unsigned char *) Tcl_AttemptAlloc(info->nbyte);
  2985. if (info->ubuf == NULL) {
  2986. merror:
  2987. if (ubuf != NULL) {
  2988. info->isenc = 0;
  2989. memset(info->keys, 0, sizeof (info->keys));
  2990. Tcl_Free((char *) ubuf);
  2991. }
  2992. Tcl_Free((char *) info);
  2993. if (interp != NULL) {
  2994. Tcl_SetObjResult(interp,
  2995. Tcl_NewStringObj("out of memory", -1));
  2996. }
  2997. goto error;
  2998. }
  2999. stream.avail_out = info->nbyte;
  3000. if (inflateInit2(&stream, -15) != Z_OK) {
  3001. goto cerror;
  3002. }
  3003. err = inflate(&stream, Z_SYNC_FLUSH);
  3004. inflateEnd(&stream);
  3005. if ((err == Z_STREAM_END) ||
  3006. ((err == Z_OK) && (stream.avail_in == 0))) {
  3007. if (ubuf != NULL) {
  3008. info->isenc = 0;
  3009. memset(info->keys, 0, sizeof (info->keys));
  3010. Tcl_Free((char *) ubuf);
  3011. }
  3012. goto wrapchan;
  3013. }
  3014. cerror:
  3015. if (ubuf != NULL) {
  3016. info->isenc = 0;
  3017. memset(info->keys, 0, sizeof (info->keys));
  3018. Tcl_Free((char *) ubuf);
  3019. }
  3020. if (info->ubuf != NULL) {
  3021. Tcl_Free((char *) info->ubuf);
  3022. }
  3023. Tcl_Free((char *) info);
  3024. if (interp != NULL) {
  3025. Tcl_SetObjResult(interp,
  3026. Tcl_NewStringObj("decompression error", -1));
  3027. }
  3028. goto error;
  3029. }
  3030. }
  3031. wrapchan:
  3032. sprintf(cname, "zipfs_%lx_%d", (unsigned long) z->offset, ZipFS.idCount++);
  3033. z->zipfile->nopen++;
  3034. Unlock();
  3035. return Tcl_CreateChannel(&ZipChannelType, cname, (ClientData) info, flags);
  3036.  
  3037. error:
  3038. Unlock();
  3039. return NULL;
  3040. }
  3041.  
  3042. /*
  3043. *-------------------------------------------------------------------------
  3044. *
  3045. * ZipEntryStat --
  3046. *
  3047. * This function implements the ZIP filesystem specific version
  3048. * of the library version of stat.
  3049. *
  3050. * Results:
  3051. * See stat documentation.
  3052. *
  3053. * Side effects:
  3054. * See stat documentation.
  3055. *
  3056. *-------------------------------------------------------------------------
  3057. */
  3058.  
  3059. static int
  3060. ZipEntryStat(char *path, Tcl_StatBuf *buf)
  3061. {
  3062. ZipEntry *z;
  3063. int ret = -1;
  3064.  
  3065. ReadLock();
  3066. z = ZipFSLookup(path);
  3067. if (z == NULL) {
  3068. goto done;
  3069. }
  3070. memset(buf, 0, sizeof (Tcl_StatBuf));
  3071. if (z->isdir) {
  3072. buf->st_mode = S_IFDIR | 0555;
  3073. } else {
  3074. buf->st_mode = S_IFREG | 0555;
  3075. }
  3076. buf->st_size = z->nbyte;
  3077. buf->st_mtime = z->timestamp;
  3078. buf->st_ctime = z->timestamp;
  3079. buf->st_atime = z->timestamp;
  3080. ret = 0;
  3081. done:
  3082. Unlock();
  3083. return ret;
  3084. }
  3085.  
  3086. /*
  3087. *-------------------------------------------------------------------------
  3088. *
  3089. * ZipEntryAccess --
  3090. *
  3091. * This function implements the ZIP filesystem specific version
  3092. * of the library version of access.
  3093. *
  3094. * Results:
  3095. * See access documentation.
  3096. *
  3097. * Side effects:
  3098. * See access documentation.
  3099. *
  3100. *-------------------------------------------------------------------------
  3101. */
  3102.  
  3103. static int
  3104. ZipEntryAccess(char *path, int mode)
  3105. {
  3106. ZipEntry *z;
  3107.  
  3108. if (mode & 3) {
  3109. return -1;
  3110. }
  3111. ReadLock();
  3112. z = ZipFSLookup(path);
  3113. Unlock();
  3114. return (z != NULL) ? 0 : -1;
  3115. }
  3116.  
  3117. /*
  3118. *-------------------------------------------------------------------------
  3119. *
  3120. * Zip_FSOpenFileChannelProc --
  3121. *
  3122. * Results:
  3123. *
  3124. * Side effects:
  3125. *
  3126. *-------------------------------------------------------------------------
  3127. */
  3128.  
  3129. static Tcl_Channel
  3130. Zip_FSOpenFileChannelProc(Tcl_Interp *interp, Tcl_Obj *pathPtr,
  3131. int mode, int permissions)
  3132. {
  3133. int len;
  3134.  
  3135. if (!(pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr))) return NULL;
  3136.  
  3137. return ZipChannelOpen(interp, Tcl_GetStringFromObj(pathPtr, &len),
  3138. mode, permissions);
  3139. }
  3140.  
  3141. /*
  3142. *-------------------------------------------------------------------------
  3143. *
  3144. * Zip_FSStatProc --
  3145. *
  3146. * This function implements the ZIP filesystem specific version
  3147. * of the library version of stat.
  3148. *
  3149. * Results:
  3150. * See stat documentation.
  3151. *
  3152. * Side effects:
  3153. * See stat documentation.
  3154. *
  3155. *-------------------------------------------------------------------------
  3156. */
  3157.  
  3158. static int
  3159. Zip_FSStatProc(Tcl_Obj *pathPtr, Tcl_StatBuf *buf)
  3160. {
  3161. int len;
  3162.  
  3163. if (!(pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr))) return -1;
  3164.  
  3165. return ZipEntryStat(Tcl_GetStringFromObj(pathPtr, &len), buf);
  3166. }
  3167.  
  3168. /*
  3169. *-------------------------------------------------------------------------
  3170. *
  3171. * Zip_FSAccessProc --
  3172. *
  3173. * This function implements the ZIP filesystem specific version
  3174. * of the library version of access.
  3175. *
  3176. * Results:
  3177. * See access documentation.
  3178. *
  3179. * Side effects:
  3180. * See access documentation.
  3181. *
  3182. *-------------------------------------------------------------------------
  3183. */
  3184.  
  3185. static int
  3186. Zip_FSAccessProc(Tcl_Obj *pathPtr, int mode)
  3187. {
  3188. int len;
  3189.  
  3190. if (!(pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr))) return -1;
  3191.  
  3192. return ZipEntryAccess(Tcl_GetStringFromObj(pathPtr, &len), mode);
  3193. }
  3194.  
  3195. /*
  3196. *-------------------------------------------------------------------------
  3197. *
  3198. * Zip_FSFilesystemSeparatorProc --
  3199. *
  3200. * This function returns the separator to be used for a given path. The
  3201. * object returned should have a refCount of zero
  3202. *
  3203. * Results:
  3204. * A Tcl object, with a refCount of zero. If the caller needs to retain a
  3205. * reference to the object, it should call Tcl_IncrRefCount, and should
  3206. * otherwise free the object.
  3207. *
  3208. * Side effects:
  3209. * None.
  3210. *
  3211. *-------------------------------------------------------------------------
  3212. */
  3213.  
  3214. static Tcl_Obj *
  3215. Zip_FSFilesystemSeparatorProc(Tcl_Obj *pathPtr)
  3216. {
  3217. if(!zipfs_literal_fsseparator) {
  3218. zipfs_literal_fsseparator=Tcl_NewStringObj("/", -1);
  3219. Tcl_IncrRefCount(zipfs_literal_fsseparator);
  3220. }
  3221. Tcl_IncrRefCount(zipfs_literal_fsseparator);
  3222. return zipfs_literal_fsseparator;
  3223. }
  3224.  
  3225. /*
  3226. *-------------------------------------------------------------------------
  3227. *
  3228. * Zip_FSMatchInDirectoryProc --
  3229. *
  3230. * This routine is used by the globbing code to search a directory for
  3231. * all files which match a given pattern.
  3232. *
  3233. * Results:
  3234. * The return value is a standard Tcl result indicating whether an
  3235. * error occurred in globbing. Errors are left in interp, good
  3236. * results are lappend'ed to resultPtr (which must be a valid object).
  3237. *
  3238. * Side effects:
  3239. * None.
  3240. *
  3241. *-------------------------------------------------------------------------
  3242. */
  3243. static int
  3244. Zip_FSMatchInDirectoryProc(Tcl_Interp* interp, Tcl_Obj *result,
  3245. Tcl_Obj *pathPtr, const char *pattern,
  3246. Tcl_GlobTypeData *types)
  3247. {
  3248. Tcl_HashEntry *hPtr;
  3249. Tcl_HashSearch search;
  3250. Tcl_Obj *normPathPtr;
  3251. int scnt, len, l, dirOnly = -1, prefixLen, strip = 0;
  3252. char *pat, *prefix, *path;
  3253. Tcl_DString dsPref;
  3254.  
  3255. if (!(normPathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr))) return -1;
  3256.  
  3257. if (types != NULL) {
  3258. dirOnly = (types->type & TCL_GLOB_TYPE_DIR) == TCL_GLOB_TYPE_DIR;
  3259. }
  3260.  
  3261. /* the prefix that gets prepended to results */
  3262. prefix = Tcl_GetStringFromObj(pathPtr, &prefixLen);
  3263.  
  3264. /* the (normalized) path we're searching */
  3265. path = Tcl_GetStringFromObj(normPathPtr, &len);
  3266.  
  3267. Tcl_DStringInit(&dsPref);
  3268. Tcl_DStringAppend(&dsPref, prefix, prefixLen);
  3269.  
  3270. if (strcmp(prefix, path) == 0) {
  3271. prefix = NULL;
  3272. } else {
  3273. strip = len + 1;
  3274. }
  3275. if (prefix != NULL) {
  3276. Tcl_DStringAppend(&dsPref, "/", 1);
  3277. prefixLen++;
  3278. prefix = Tcl_DStringValue(&dsPref);
  3279. }
  3280. ReadLock();
  3281. if ((types != NULL) && (types->type == TCL_GLOB_TYPE_MOUNT)) {
  3282. l = CountSlashes(path);
  3283. if (path[len - 1] == '/') {
  3284. len--;
  3285. } else {
  3286. l++;
  3287. }
  3288. if ((pattern == NULL) || (pattern[0] == '\0')) {
  3289. pattern = "*";
  3290. }
  3291. hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search);
  3292. while (hPtr != NULL) {
  3293. ZipFile *zf = (ZipFile *) Tcl_GetHashValue(hPtr);
  3294.  
  3295. if (zf->mntptlen == 0) {
  3296. ZipEntry *z = zf->topents;
  3297. while (z != NULL) {
  3298. int lenz = strlen(z->name);
  3299. if ((lenz > len + 1) &&
  3300. (strncmp(z->name, path, len) == 0) &&
  3301. (z->name[len] == '/') &&
  3302. (CountSlashes(z->name) == l) &&
  3303. Tcl_StringCaseMatch(z->name + len + 1, pattern, 0)) {
  3304. if (prefix != NULL) {
  3305. Tcl_DStringAppend(&dsPref, z->name, lenz);
  3306. Tcl_ListObjAppendElement(NULL, result,
  3307. Tcl_NewStringObj(Tcl_DStringValue(&dsPref),
  3308. Tcl_DStringLength(&dsPref)));
  3309. Tcl_DStringSetLength(&dsPref, prefixLen);
  3310. } else {
  3311. Tcl_ListObjAppendElement(NULL, result,
  3312. Tcl_NewStringObj(z->name, lenz));
  3313. }
  3314. }
  3315. z = z->tnext;
  3316. }
  3317. } else if ((zf->mntptlen > len + 1) &&
  3318. (strncmp(zf->mntpt, path, len) == 0) &&
  3319. (zf->mntpt[len] == '/') &&
  3320. (CountSlashes(zf->mntpt) == l) &&
  3321. Tcl_StringCaseMatch(zf->mntpt + len + 1, pattern, 0)) {
  3322. if (prefix != NULL) {
  3323. Tcl_DStringAppend(&dsPref, zf->mntpt, zf->mntptlen);
  3324. Tcl_ListObjAppendElement(NULL, result,
  3325. Tcl_NewStringObj(Tcl_DStringValue(&dsPref),
  3326. Tcl_DStringLength(&dsPref)));
  3327. Tcl_DStringSetLength(&dsPref, prefixLen);
  3328. } else {
  3329. Tcl_ListObjAppendElement(NULL, result,
  3330. Tcl_NewStringObj(zf->mntpt, zf->mntptlen));
  3331. }
  3332. }
  3333. hPtr = Tcl_NextHashEntry(&search);
  3334. }
  3335. goto end;
  3336. }
  3337. if ((pattern == NULL) || (pattern[0] == '\0')) {
  3338. hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, path);
  3339. if (hPtr != NULL) {
  3340. ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
  3341.  
  3342. if ((dirOnly < 0) ||
  3343. (!dirOnly && !z->isdir) ||
  3344. (dirOnly && z->isdir)) {
  3345. if (prefix != NULL) {
  3346. Tcl_DStringAppend(&dsPref, z->name, -1);
  3347. Tcl_ListObjAppendElement(NULL, result,
  3348. Tcl_NewStringObj(Tcl_DStringValue(&dsPref),
  3349. Tcl_DStringLength(&dsPref)));
  3350. Tcl_DStringSetLength(&dsPref, prefixLen);
  3351. } else {
  3352. Tcl_ListObjAppendElement(NULL, result,
  3353. Tcl_NewStringObj(z->name, -1));
  3354. }
  3355. }
  3356. }
  3357. goto end;
  3358. }
  3359. l = strlen(pattern);
  3360. pat = Tcl_Alloc(len + l + 2);
  3361. memcpy(pat, path, len);
  3362. while ((len > 1) && (pat[len - 1] == '/')) {
  3363. --len;
  3364. }
  3365. if ((len > 1) || (pat[0] != '/')) {
  3366. pat[len] = '/';
  3367. ++len;
  3368. }
  3369. memcpy(pat + len, pattern, l + 1);
  3370. scnt = CountSlashes(pat);
  3371. for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
  3372. hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  3373. ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
  3374. if ((dirOnly >= 0) &&
  3375. ((dirOnly && !z->isdir) || (!dirOnly && z->isdir))) {
  3376. continue;
  3377. }
  3378. if ((z->depth == scnt) && Tcl_StringCaseMatch(z->name, pat, 0)) {
  3379. if (prefix != NULL) {
  3380. Tcl_DStringAppend(&dsPref, z->name + strip, -1);
  3381. Tcl_ListObjAppendElement(NULL, result,
  3382. Tcl_NewStringObj(Tcl_DStringValue(&dsPref),
  3383. Tcl_DStringLength(&dsPref)));
  3384. Tcl_DStringSetLength(&dsPref, prefixLen);
  3385. } else {
  3386. Tcl_ListObjAppendElement(NULL, result,
  3387. Tcl_NewStringObj(z->name + strip, -1));
  3388. }
  3389. }
  3390. }
  3391. Tcl_Free(pat);
  3392. end:
  3393. Unlock();
  3394. Tcl_DStringFree(&dsPref);
  3395. return TCL_OK;
  3396. }
  3397.  
  3398. /*
  3399. *-------------------------------------------------------------------------
  3400. *
  3401. * Zip_FSPathInFilesystemProc --
  3402. *
  3403. * This function determines if the given path object is in the
  3404. * ZIP filesystem.
  3405. *
  3406. * Results:
  3407. * TCL_OK when the path object is in the ZIP filesystem, -1 otherwise.
  3408. *
  3409. * Side effects:
  3410. * None.
  3411. *
  3412. *-------------------------------------------------------------------------
  3413. */
  3414.  
  3415. static int
  3416. Zip_FSPathInFilesystemProc(Tcl_Obj *pathPtr, ClientData *clientDataPtr)
  3417. {
  3418. Tcl_HashEntry *hPtr;
  3419. Tcl_HashSearch search;
  3420. ZipFile *zf;
  3421. int ret = -1, len;
  3422. char *path;
  3423.  
  3424. if (!(pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr))) return -1;
  3425.  
  3426. path = Tcl_GetStringFromObj(pathPtr, &len);
  3427. if(strncmp(path,ZIPFS_VOLUME,ZIPFS_VOLUME_LEN)!=0) {
  3428. return -1;
  3429. }
  3430.  
  3431. len = strlen(path);
  3432.  
  3433. ReadLock();
  3434. hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, path);
  3435. if (hPtr != NULL) {
  3436. ret = TCL_OK;
  3437. goto endloop;
  3438. }
  3439. hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search);
  3440. while (hPtr != NULL) {
  3441. zf = (ZipFile *) Tcl_GetHashValue(hPtr);
  3442. if (zf->mntptlen == 0) {
  3443. ZipEntry *z = zf->topents;
  3444. while (z != NULL) {
  3445. int lenz = strlen(z->name);
  3446.  
  3447. if ((len >= lenz) &&
  3448. (strncmp(path, z->name, lenz) == 0)) {
  3449. ret = TCL_OK;
  3450. goto endloop;
  3451. }
  3452. z = z->tnext;
  3453. }
  3454. } else if ((len >= zf->mntptlen) &&
  3455. (strncmp(path, zf->mntpt, zf->mntptlen) == 0)) {
  3456. ret = TCL_OK;
  3457. goto endloop;
  3458. }
  3459. hPtr = Tcl_NextHashEntry(&search);
  3460. }
  3461. endloop:
  3462. Unlock();
  3463. return ret;
  3464. }
  3465.  
  3466. /*
  3467. *-------------------------------------------------------------------------
  3468. *
  3469. * Zip_FSListVolumesProc --
  3470. *
  3471. * Lists the currently mounted ZIP filesystem volumes.
  3472. *
  3473. * Results:
  3474. * The list of volumes.
  3475. *
  3476. * Side effects:
  3477. * None
  3478. *
  3479. *-------------------------------------------------------------------------
  3480. */
  3481. static Tcl_Obj *
  3482. Zip_FSListVolumesProc(void) {
  3483. if(!zipfs_literal_fsroot) {
  3484. zipfs_literal_fsroot=Tcl_NewStringObj(ZIPFS_VOLUME, -1);
  3485. Tcl_IncrRefCount(zipfs_literal_fsroot);
  3486. }
  3487. Tcl_IncrRefCount(zipfs_literal_fsroot);
  3488. return zipfs_literal_fsroot;
  3489. }
  3490.  
  3491. /*
  3492. *-------------------------------------------------------------------------
  3493. *
  3494. * Zip_FSFileAttrStringsProc --
  3495. *
  3496. * This function implements the ZIP filesystem dependent 'file attributes'
  3497. * subcommand, for listing the set of possible attribute strings.
  3498. *
  3499. * Results:
  3500. * An array of strings
  3501. *
  3502. * Side effects:
  3503. * None.
  3504. *
  3505. *-------------------------------------------------------------------------
  3506. */
  3507.  
  3508. static const char *const *
  3509. Zip_FSFileAttrStringsProc(Tcl_Obj *pathPtr, Tcl_Obj** objPtrRef)
  3510. {
  3511. static const char *const attrs[] = {
  3512. "-uncompsize",
  3513. "-compsize",
  3514. "-offset",
  3515. "-mount",
  3516. "-archive",
  3517. "-permissions",
  3518. NULL,
  3519. };
  3520.  
  3521. return attrs;
  3522. }
  3523.  
  3524. /*
  3525. *-------------------------------------------------------------------------
  3526. *
  3527. * Zip_FSFileAttrsGetProc --
  3528. *
  3529. * This function implements the ZIP filesystem specific
  3530. * 'file attributes' subcommand, for 'get' operations.
  3531. *
  3532. * Results:
  3533. * Standard Tcl return code. The object placed in objPtrRef (if TCL_OK
  3534. * was returned) is likely to have a refCount of zero. Either way we must
  3535. * either store it somewhere (e.g. the Tcl result), or Incr/Decr its
  3536. * refCount to ensure it is properly freed.
  3537. *
  3538. * Side effects:
  3539. * None.
  3540. *
  3541. *-------------------------------------------------------------------------
  3542. */
  3543.  
  3544. static int
  3545. Zip_FSFileAttrsGetProc(Tcl_Interp *interp, int index, Tcl_Obj *pathPtr,
  3546. Tcl_Obj **objPtrRef)
  3547. {
  3548. int len, ret = TCL_OK;
  3549. char *path;
  3550. ZipEntry *z;
  3551.  
  3552. if (!(pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr))) return -1;
  3553.  
  3554. path = Tcl_GetStringFromObj(pathPtr, &len);
  3555. ReadLock();
  3556. z = ZipFSLookup(path);
  3557. if (z == NULL) {
  3558. if (interp != NULL) {
  3559. Tcl_SetObjResult(interp, Tcl_NewStringObj("file not found", -1));
  3560. }
  3561. ret = TCL_ERROR;
  3562. goto done;
  3563. }
  3564. switch (index) {
  3565. case 0:
  3566. *objPtrRef = Tcl_NewIntObj(z->nbyte);
  3567. goto done;
  3568. case 1:
  3569. *objPtrRef= Tcl_NewIntObj(z->nbytecompr);
  3570. goto done;
  3571. case 2:
  3572. *objPtrRef= Tcl_NewLongObj(z->offset);
  3573. goto done;
  3574. case 3:
  3575. *objPtrRef= Tcl_NewStringObj(z->zipfile->mntpt, -1);
  3576. goto done;
  3577. case 4:
  3578. *objPtrRef= Tcl_NewStringObj(z->zipfile->name, -1);
  3579. goto done;
  3580. case 5:
  3581. *objPtrRef= Tcl_NewStringObj("0555", -1);
  3582. goto done;
  3583. }
  3584. if (interp != NULL) {
  3585. Tcl_SetObjResult(interp, Tcl_NewStringObj("unknown attribute", -1));
  3586. }
  3587. ret = TCL_ERROR;
  3588. done:
  3589. Unlock();
  3590. return ret;
  3591. }
  3592.  
  3593. /*
  3594. *-------------------------------------------------------------------------
  3595. *
  3596. * Zip_FSFileAttrsSetProc --
  3597. *
  3598. * This function implements the ZIP filesystem specific
  3599. * 'file attributes' subcommand, for 'set' operations.
  3600. *
  3601. * Results:
  3602. * Standard Tcl return code.
  3603. *
  3604. * Side effects:
  3605. * None.
  3606. *
  3607. *-------------------------------------------------------------------------
  3608. */
  3609.  
  3610. static int
  3611. Zip_FSFileAttrsSetProc(Tcl_Interp *interp, int index, Tcl_Obj *pathPtr,
  3612. Tcl_Obj *objPtr)
  3613. {
  3614. if (interp != NULL) {
  3615. Tcl_SetObjResult(interp, Tcl_NewStringObj("unsupported operation", -1));
  3616. }
  3617. return TCL_ERROR;
  3618. }
  3619.  
  3620. /*
  3621. *-------------------------------------------------------------------------
  3622. *
  3623. * Zip_FSFilesystemPathTypeProc --
  3624. *
  3625. * Results:
  3626. *
  3627. * Side effects:
  3628. *
  3629. *-------------------------------------------------------------------------
  3630. */
  3631.  
  3632. static Tcl_Obj *
  3633. Zip_FSFilesystemPathTypeProc(Tcl_Obj *pathPtr)
  3634. {
  3635. if(!zipfs_literal_fstype) {
  3636. zipfs_literal_fstype=Tcl_NewStringObj("zip", -1);
  3637. Tcl_IncrRefCount(zipfs_literal_fstype);
  3638. }
  3639. Tcl_IncrRefCount(zipfs_literal_fstype);
  3640. return zipfs_literal_fstype;
  3641. }
  3642.  
  3643.  
  3644. /*
  3645. *-------------------------------------------------------------------------
  3646. *
  3647. * Zip_FSLoadFile --
  3648. *
  3649. * This functions deals with loading native object code. If
  3650. * the given path object refers to a file within the ZIP
  3651. * filesystem, an approriate error code is returned to delegate
  3652. * loading to the caller (by copying the file to temp store
  3653. * and loading from there). As fallback when the file refers
  3654. * to the ZIP file system but is not present, it is looked up
  3655. * relative to the executable and loaded from there when available.
  3656. *
  3657. * Results:
  3658. * TCL_OK on success, -1 otherwise with error number set.
  3659. *
  3660. * Side effects:
  3661. * Loads native code into the process address space.
  3662. *
  3663. *-------------------------------------------------------------------------
  3664. */
  3665.  
  3666. static int
  3667. Zip_FSLoadFile(Tcl_Interp *interp, Tcl_Obj *path, Tcl_LoadHandle *loadHandle,
  3668. Tcl_FSUnloadFileProc **unloadProcPtr, int flags)
  3669. {
  3670. Tcl_FSLoadFileProc2 *loadFileProc;
  3671. #ifdef ANDROID
  3672. /*
  3673. * Force loadFileProc to native implementation since the
  3674. * package manger already extracted the shared libraries
  3675. * from the APK at install time.
  3676. */
  3677.  
  3678. loadFileProc = (Tcl_FSLoadFileProc2 *) tclNativeFilesystem.loadFileProc;
  3679. if (loadFileProc != NULL) {
  3680. return loadFileProc(interp, path, loadHandle, unloadProcPtr, flags);
  3681. }
  3682. Tcl_SetErrno(ENOENT);
  3683. return -1;
  3684. #else
  3685. Tcl_Obj *altPath = NULL;
  3686. int ret = -1;
  3687.  
  3688. if (Tcl_FSAccess(path, R_OK) == 0) {
  3689. /*
  3690. * EXDEV should trigger loading by copying to temp store.
  3691. */
  3692. Tcl_SetErrno(EXDEV);
  3693. return ret;
  3694. } else {
  3695. Tcl_Obj *objs[2] = { NULL, NULL };
  3696.  
  3697. objs[1] = TclPathPart(interp, path, TCL_PATH_DIRNAME);
  3698. if ((objs[1] != NULL) && (Zip_FSAccessProc(objs[1], R_OK) == 0)) {
  3699. const char *execName = Tcl_GetNameOfExecutable();
  3700.  
  3701. /*
  3702. * Shared object is not in ZIP but its path prefix is,
  3703. * thus try to load from directory where the executable
  3704. * came from.
  3705. */
  3706. TclDecrRefCount(objs[1]);
  3707. objs[1] = TclPathPart(interp, path, TCL_PATH_TAIL);
  3708. /*
  3709. * Get directory name of executable manually to deal
  3710. * with cases where [file dirname [info nameofexecutable]]
  3711. * is equal to [info nameofexecutable] due to VFS effects.
  3712. */
  3713. if (execName != NULL) {
  3714. const char *p = strrchr(execName, '/');
  3715.  
  3716. if (p > execName + 1) {
  3717. --p;
  3718. objs[0] = Tcl_NewStringObj(execName, p - execName);
  3719. }
  3720. }
  3721. if (objs[0] == NULL) {
  3722. objs[0] = TclPathPart(interp, TclGetObjNameOfExecutable(),
  3723. TCL_PATH_DIRNAME);
  3724. }
  3725. if (objs[0] != NULL) {
  3726. altPath = TclJoinPath(2, objs);
  3727. if (altPath != NULL) {
  3728. Tcl_IncrRefCount(altPath);
  3729. if (Tcl_FSAccess(altPath, R_OK) == 0) {
  3730. path = altPath;
  3731. }
  3732. }
  3733. }
  3734. }
  3735. if (objs[0] != NULL) {
  3736. Tcl_DecrRefCount(objs[0]);
  3737. }
  3738. if (objs[1] != NULL) {
  3739. Tcl_DecrRefCount(objs[1]);
  3740. }
  3741. }
  3742. loadFileProc = (Tcl_FSLoadFileProc2 *) tclNativeFilesystem.loadFileProc;
  3743. if (loadFileProc != NULL) {
  3744. ret = loadFileProc(interp, path, loadHandle, unloadProcPtr, flags);
  3745. } else {
  3746. Tcl_SetErrno(ENOENT);
  3747. }
  3748. if (altPath != NULL) {
  3749. Tcl_DecrRefCount(altPath);
  3750. }
  3751. return ret;
  3752. #endif
  3753. }
  3754.  
  3755.  
  3756. /*
  3757. * Define the ZIP filesystem dispatch table.
  3758. */
  3759.  
  3760. MODULE_SCOPE const Tcl_Filesystem zipfsFilesystem;
  3761.  
  3762. const Tcl_Filesystem zipfsFilesystem = {
  3763. "zipfs",
  3764. sizeof (Tcl_Filesystem),
  3765. TCL_FILESYSTEM_VERSION_2,
  3766. Zip_FSPathInFilesystemProc,
  3767. NULL, /* dupInternalRepProc */
  3768. NULL, /* freeInternalRepProc */
  3769. NULL, /* internalToNormalizedProc */
  3770. NULL, /* createInternalRepProc */
  3771. NULL, /* normalizePathProc */
  3772. Zip_FSFilesystemPathTypeProc,
  3773. Zip_FSFilesystemSeparatorProc,
  3774. Zip_FSStatProc,
  3775. Zip_FSAccessProc,
  3776. Zip_FSOpenFileChannelProc,
  3777. Zip_FSMatchInDirectoryProc,
  3778. NULL, /* utimeProc */
  3779. NULL, /* linkProc */
  3780. Zip_FSListVolumesProc,
  3781. Zip_FSFileAttrStringsProc,
  3782. Zip_FSFileAttrsGetProc,
  3783. Zip_FSFileAttrsSetProc,
  3784. NULL, /* createDirectoryProc */
  3785. NULL, /* removeDirectoryProc */
  3786. NULL, /* deleteFileProc */
  3787. NULL, /* copyFileProc */
  3788. NULL, /* renameFileProc */
  3789. NULL, /* copyDirectoryProc */
  3790. NULL, /* lstatProc */
  3791. (Tcl_FSLoadFileProc *) Zip_FSLoadFile,
  3792. NULL, /* getCwdProc */
  3793. NULL, /* chdirProc*/
  3794. };
  3795.  
  3796. #endif /* HAVE_ZLIB */
  3797.  
  3798.  
  3799.  
  3800. /*
  3801. *-------------------------------------------------------------------------
  3802. *
  3803. * TclZipfs_Init --
  3804. *
  3805. * Perform per interpreter initialization of this module.
  3806. *
  3807. * Results:
  3808. * The return value is a standard Tcl result.
  3809. *
  3810. * Side effects:
  3811. * Initializes this module if not already initialized, and adds
  3812. * module related commands to the given interpreter.
  3813. *
  3814. *-------------------------------------------------------------------------
  3815. */
  3816.  
  3817. MODULE_SCOPE int
  3818. TclZipfs_Init(Tcl_Interp *interp)
  3819. {
  3820. #ifdef HAVE_ZLIB
  3821. /* one-time initialization */
  3822. WriteLock();
  3823. Tcl_StaticPackage(interp, "zipfs", TclZipfs_Init, TclZipfs_Init);
  3824. if (!ZipFS.initialized) {
  3825. #ifdef TCL_THREADS
  3826. static const Tcl_Time t = { 0, 0 };
  3827. /*
  3828. * Inflate condition variable.
  3829. */
  3830. Tcl_MutexLock(&ZipFSMutex);
  3831. Tcl_ConditionWait(&ZipFSCond, &ZipFSMutex, &t);
  3832. Tcl_MutexUnlock(&ZipFSMutex);
  3833. #endif
  3834. Tcl_FSRegister(NULL, &zipfsFilesystem);
  3835. Tcl_InitHashTable(&ZipFS.fileHash, TCL_STRING_KEYS);
  3836. Tcl_InitHashTable(&ZipFS.zipHash, TCL_STRING_KEYS);
  3837. ZipFS.initialized = ZipFS.idCount = 1;
  3838. }
  3839. Unlock();
  3840. if(interp != NULL) {
  3841. static const EnsembleImplMap initMap[] = {
  3842. {"mount", ZipFSMountObjCmd, NULL, NULL, NULL, 0},
  3843. {"unmount", ZipFSUnmountObjCmd, NULL, NULL, NULL, 0},
  3844. {"mkkey", ZipFSMkKeyObjCmd, NULL, NULL, NULL, 0},
  3845. {"mkimg", ZipFSMkImgObjCmd, NULL, NULL, NULL, 0},
  3846. {"mkzip", ZipFSMkZipObjCmd, NULL, NULL, NULL, 0},
  3847. {"lmkimg", ZipFSLMkImgObjCmd, NULL, NULL, NULL, 0},
  3848. {"lmkzip", ZipFSLMkZipObjCmd, NULL, NULL, NULL, 0},
  3849. {"exists", ZipFSExistsObjCmd, NULL, NULL, NULL, 1},
  3850. {"info", ZipFSInfoObjCmd, NULL, NULL, NULL, 1},
  3851. {"list", ZipFSListObjCmd, NULL, NULL, NULL, 1},
  3852. {"canonical", ZipFSCanonicalObjCmd, NULL, NULL, NULL, 1},
  3853. {"root", ZipFSRootObjCmd, NULL, NULL, NULL, 1},
  3854.  
  3855. {NULL, NULL, NULL, NULL, NULL, 0}
  3856. };
  3857. static const char findproc[] =
  3858. "namespace eval zipfs {}\n"
  3859. "proc ::zipfs::find dir {\n"
  3860. " set result {}\n"
  3861. " if {[catch {glob -directory $dir -tails -nocomplain * .*} list]} {\n"
  3862. " return $result\n"
  3863. " }\n"
  3864. " foreach file $list {\n"
  3865. " if {$file eq \".\" || $file eq \"..\"} {\n"
  3866. " continue\n"
  3867. " }\n"
  3868. " set file [file join $dir $file]\n"
  3869. " lappend result $file\n"
  3870. " foreach file [::zipfs::find $file] {\n"
  3871. " lappend result $file\n"
  3872. " }\n"
  3873. " }\n"
  3874. " return [lsort $result]\n"
  3875. "}\n";
  3876. Tcl_EvalEx(interp, findproc, -1, TCL_EVAL_GLOBAL);
  3877. Tcl_LinkVar(interp, "::zipfs::wrmax", (char *) &ZipFS.wrmax,
  3878. TCL_LINK_INT);
  3879. TclMakeEnsemble(interp, "zipfs", initMap);
  3880. Tcl_PkgProvide(interp, "zipfs", "2.0");
  3881. }
  3882. return TCL_OK;
  3883. #else
  3884. if (interp != NULL) {
  3885. Tcl_SetObjResult(interp, Tcl_NewStringObj("no zlib available", -1));
  3886. }
  3887. return TCL_ERROR;
  3888. #endif
  3889. }
  3890.  
  3891. static int TclZipfs_AppHook_FindTclInit(const char *archive){
  3892. Tcl_Obj *vfsinitscript;
  3893. int found;
  3894. if(TclZipfs_Mount(NULL, archive, ZIPFS_ZIP_MOUNT, NULL)) {
  3895. /* Either the file doesn't exist or it is not a zip archive */
  3896. return TCL_ERROR;
  3897. }
  3898. vfsinitscript=Tcl_NewStringObj(ZIPFS_ZIP_MOUNT "/init.tcl",-1);
  3899. Tcl_IncrRefCount(vfsinitscript);
  3900. found=Tcl_FSAccess(vfsinitscript,F_OK);
  3901. if(found==0) {
  3902. return TCL_OK;
  3903. }
  3904. Tcl_DecrRefCount(vfsinitscript);
  3905. vfsinitscript=Tcl_NewStringObj(ZIPFS_ZIP_MOUNT "/tcl_library/init.tcl",-1);
  3906. Tcl_IncrRefCount(vfsinitscript);
  3907. found=Tcl_FSAccess(vfsinitscript,F_OK);
  3908. if(found==0) {
  3909. return TCL_OK;
  3910. }
  3911. return TCL_ERROR;
  3912. }
  3913.  
  3914. int TclZipfs_AppHook(int *argc, char ***argv){
  3915. /*
  3916. * Tclkit_MainHook --
  3917. * Performs the argument munging for the shell
  3918. */
  3919.  
  3920. CONST char *archive;
  3921. Tcl_FindExecutable(*argv[0]);
  3922. archive=Tcl_GetNameOfExecutable();
  3923. TclZipfs_Init(NULL);
  3924. /*
  3925. ** Look for init.tcl in one of the locations mounted later in this function
  3926. ** and failing that, look for a file name CFG_RUNTIME_ZIPFILE adjacent to the
  3927. ** executable
  3928. */
  3929. TclSetPreInitScript(
  3930. "foreach {path} {\n"
  3931. " {" ZIPFS_APP_MOUNT "/tcl_library}\n"
  3932. " {" ZIPFS_ZIP_MOUNT "/tcl_library}\n"
  3933. "} {\n"
  3934. " if {![file exists [file join $path init.tcl]]} continue\n"
  3935. " set ::tcl_library $path\n"
  3936. " break\n"
  3937. "}\n"
  3938. "if {![info exists ::tcl_library] || $::tcl_library eq {}} {\n"
  3939. " set zipfile [file join [file dirname [info nameofexecutable]] " CFG_RUNTIME_ZIPFILE "]\n"
  3940. " if {[file exists $zipfile]} {\n"
  3941. " zipfs mount $zipfile {" ZIPFS_ZIP_MOUNT "}\n"
  3942. " if {[file exists [file join {" ZIPFS_ZIP_MOUNT "} init.tcl]]} \{\n"
  3943. " set ::tcl_library {" ZIPFS_ZIP_MOUNT "}\n"
  3944. " } else {\n"
  3945. " zipfs unmount {" ZIPFS_ZIP_MOUNT "}\n"
  3946. " }\n"
  3947. " }\n"
  3948. "}\n"
  3949. "foreach {path} {\n"
  3950. " {" ZIPFS_APP_MOUNT "/tk_library}\n"
  3951. " {" ZIPFS_ZIP_MOUNT "/tk_library}\n"
  3952. " {" ZIPFS_VOLUME "lib/tk/tk_library}\n"
  3953. "} {\n"
  3954. " if {[file exists [file join $path init.tcl]]} continue\n"
  3955. " set ::tk_library $path\n"
  3956. " break\n"
  3957. "}\n"
  3958. );
  3959. if(!TclZipfs_Mount(NULL, archive, ZIPFS_APP_MOUNT, NULL)) {
  3960. int found;
  3961. Tcl_Obj *vfsinitscript;
  3962. vfsinitscript=Tcl_NewStringObj(ZIPFS_APP_MOUNT "/main.tcl",-1);
  3963. Tcl_IncrRefCount(vfsinitscript);
  3964. if(Tcl_FSAccess(vfsinitscript,F_OK)==0) {
  3965. /* Startup script should be set before calling Tcl_AppInit */
  3966. Tcl_SetStartupScript(vfsinitscript,NULL);
  3967. } else {
  3968. Tcl_DecrRefCount(vfsinitscript);
  3969. }
  3970. /* Set Tcl Encodings */
  3971. vfsinitscript=Tcl_NewStringObj(ZIPFS_APP_MOUNT "/tcl_library/init.tcl",-1);
  3972. Tcl_IncrRefCount(vfsinitscript);
  3973. found=Tcl_FSAccess(vfsinitscript,F_OK);
  3974. Tcl_DecrRefCount(vfsinitscript);
  3975. if(found==TCL_OK) {
  3976. return TCL_OK;
  3977. }
  3978. } else if (*argc>1) {
  3979. archive=*argv[1];
  3980. printf("First arg: %s\n",archive);
  3981. fflush(stdout);
  3982. if(!TclZipfs_Mount(NULL, archive, ZIPFS_APP_MOUNT, NULL)) {
  3983. int found;
  3984. Tcl_Obj *vfsinitscript;
  3985. vfsinitscript=Tcl_NewStringObj(ZIPFS_APP_MOUNT "/main.tcl",-1);
  3986. Tcl_IncrRefCount(vfsinitscript);
  3987. if(Tcl_FSAccess(vfsinitscript,F_OK)==0) {
  3988. /* Startup script should be set before calling Tcl_AppInit */
  3989. Tcl_SetStartupScript(vfsinitscript,NULL);
  3990. } else {
  3991. Tcl_DecrRefCount(vfsinitscript);
  3992. }
  3993. /* Set Tcl Encodings */
  3994. vfsinitscript=Tcl_NewStringObj(ZIPFS_APP_MOUNT "/tcl_library/init.tcl",-1);
  3995. Tcl_IncrRefCount(vfsinitscript);
  3996. found=Tcl_FSAccess(vfsinitscript,F_OK);
  3997. Tcl_DecrRefCount(vfsinitscript);
  3998. if(found==TCL_OK) {
  3999. return TCL_OK;
  4000. }
  4001. }
  4002. }
  4003. /* Mount zip file and dll before releasing to search */
  4004. if(TclZipfs_AppHook_FindTclInit(CFG_RUNTIME_PATH "/" CFG_RUNTIME_DLLFILE)==TCL_OK) {
  4005. return TCL_OK;
  4006. }
  4007. if(TclZipfs_AppHook_FindTclInit(CFG_RUNTIME_PATH "/" CFG_RUNTIME_ZIPFILE)==TCL_OK) {
  4008. return TCL_OK;
  4009. }
  4010. return TCL_OK;
  4011. }
  4012.  
  4013.  
  4014. #ifndef HAVE_ZLIB
  4015.  
  4016. /*
  4017. *-------------------------------------------------------------------------
  4018. *
  4019. * TclZipfs_Mount, TclZipfs_Unmount --
  4020. *
  4021. * Dummy version when no ZLIB support available.
  4022. *
  4023. *-------------------------------------------------------------------------
  4024. */
  4025.  
  4026. int
  4027. TclZipfs_Mount(Tcl_Interp *interp, const char *zipname, const char *mntpt,
  4028. const char *passwd)
  4029. {
  4030. return TclZipfs_Init(interp, 1);
  4031. }
  4032.  
  4033. int
  4034. TclZipfs_Unmount(Tcl_Interp *interp, const char *zipname)
  4035. {
  4036. return TclZipfs_Init(interp, 1);
  4037. }
  4038.  
  4039. #endif
  4040.  
  4041. /*
  4042. * Local Variables:
  4043. * mode: c
  4044. * c-basic-offset: 4
  4045. * fill-column: 78
  4046. * End:
  4047. */
  4048.