misc_z.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. /*
  2. misc_z.c - miscellaneous zlib functions
  3. Copyright (c) 2001 - 2004, 2015 dbjh
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. */
  16. #ifdef HAVE_CONFIG_H
  17. #include "config.h" // USE_ZLIB
  18. #endif
  19. #ifdef USE_ZLIB
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <errno.h>
  24. #ifdef _MSC_VER
  25. #pragma warning(push)
  26. #pragma warning(disable: 4820) // 'bytes' bytes padding added after construct 'member_name'
  27. #endif
  28. #include <sys/stat.h>
  29. #ifdef _MSC_VER
  30. #pragma warning(pop)
  31. #endif
  32. #include <zlib.h>
  33. #include "misc_z.h"
  34. #include "misc.h"
  35. #include "map.h"
  36. #include "unzip.h"
  37. #if defined DJGPP && defined DLL
  38. #include "dxedll_priv.h"
  39. #endif
  40. int
  41. q_fsize (const char *filename)
  42. // If USE_ZLIB is defined this function is very slow. Please avoid to use
  43. // it much.
  44. {
  45. FILE *file;
  46. unsigned char magic[4] = { 0 };
  47. #undef fopen
  48. #undef fread
  49. #undef fclose
  50. if ((file = fopen (filename, "rb")) == NULL)
  51. {
  52. errno = ENOENT;
  53. return -1;
  54. }
  55. fread (magic, 1, sizeof (magic), file);
  56. fclose (file);
  57. #define fopen fopen2
  58. #define fclose fclose2
  59. #define fread fread2
  60. if (magic[0] == 0x1f && magic[1] == 0x8b && magic[2] == 0x08)
  61. { // ID1, ID2 and CM. gzip uses Compression Method 8
  62. int size = 0;
  63. if ((file = (FILE *) gzopen (filename, "rb")) == NULL)
  64. { // Shouldn't fail because we could open it with fopen()
  65. errno = ENOENT;
  66. return -1;
  67. }
  68. #if 1
  69. // This is not much faster than the method below
  70. while (!gzeof ((gzFile) file))
  71. {
  72. gzseek ((gzFile) file, 1024 * 1024, SEEK_CUR);
  73. gzgetc ((gzFile) file); // necessary in order to set EOF (zlib 1.2.8)
  74. }
  75. #else
  76. // Is there a more efficient way to determine the uncompressed size?
  77. unsigned char buf[MAXBUFSIZE];
  78. while (gzread ((gzFile) file, buf, MAXBUFSIZE) > 0)
  79. ;
  80. #endif
  81. size = gztell ((gzFile) file);
  82. gzclose ((gzFile) file);
  83. return size;
  84. }
  85. else if (magic[0] == 'P' && magic[1] == 'K' && magic[2] == 0x03 && magic[3] == 0x04)
  86. {
  87. unz_file_info info;
  88. if ((file = (FILE *) unzOpen (filename)) == NULL)
  89. {
  90. errno = ENOENT;
  91. return -1;
  92. }
  93. unzip_goto_file (file, unzip_current_file_nr);
  94. unzGetCurrentFileInfo (file, &info, NULL, 0, NULL, 0, NULL, 0);
  95. unzClose (file);
  96. return info.uncompressed_size;
  97. }
  98. else
  99. {
  100. struct stat fstate;
  101. if (!stat (filename, &fstate))
  102. return fstate.st_size;
  103. errno = ENOENT;
  104. return -1;
  105. }
  106. }
  107. /*
  108. The zlib functions gzwrite() and gzputc() write compressed data if the file
  109. was opened with a "w" in the mode string, but we want to control whether we
  110. write compressed data. Note that for the mode string "w+", zlib ignores the
  111. '+'. zlib does the same for "r+".
  112. Currently we never write compressed data. That is, no code fopen()s files
  113. with a mode string containing 'f', 'h' or a number, but writing compressed
  114. output does work.
  115. */
  116. st_map_t *fh_map = NULL; // associative array: file handle -> file mode
  117. typedef enum { FM_NORMAL, FM_GZIP, FM_ZIP, FM_UNDEF } fmode2_t;
  118. typedef struct st_finfo
  119. {
  120. fmode2_t fmode;
  121. int compressed;
  122. } st_finfo_t;
  123. static st_finfo_t finfo_list[6] = { {FM_NORMAL, 0},
  124. {FM_NORMAL, 1}, // should never be used
  125. {FM_GZIP, 0},
  126. {FM_GZIP, 1},
  127. {FM_ZIP, 0}, // should never be used
  128. {FM_ZIP, 1} };
  129. int unzip_current_file_nr = 0;
  130. static void
  131. init_fh_map (void)
  132. {
  133. fh_map = map_create (20); // 20 simultaneous open files
  134. map_put (fh_map, stdin, &finfo_list[0]); // should be enough to start with
  135. map_put (fh_map, stdout, &finfo_list[0]);
  136. map_put (fh_map, stderr, &finfo_list[0]);
  137. }
  138. static st_finfo_t *
  139. get_finfo (FILE *file)
  140. {
  141. st_finfo_t *finfo;
  142. if (fh_map == NULL)
  143. init_fh_map ();
  144. if ((finfo = (st_finfo_t *) map_get (fh_map, file)) == NULL)
  145. {
  146. fprintf (stderr, "\nINTERNAL ERROR: File pointer was not present in map (%p)\n", file);
  147. map_dump (fh_map);
  148. exit (1);
  149. }
  150. return finfo;
  151. }
  152. static fmode2_t
  153. get_fmode (FILE *file)
  154. {
  155. return get_finfo (file)->fmode;
  156. }
  157. int
  158. unzip_get_number_entries (const char *filename)
  159. {
  160. FILE *file;
  161. unsigned char magic[4] = { 0 };
  162. #undef fopen
  163. #undef fread
  164. #undef fclose
  165. if ((file = fopen (filename, "rb")) == NULL)
  166. {
  167. errno = ENOENT;
  168. return -1;
  169. }
  170. fread (magic, 1, sizeof (magic), file);
  171. fclose (file);
  172. #define fopen fopen2
  173. #define fclose fclose2
  174. #define fread fread2
  175. if (magic[0] == 'P' && magic[1] == 'K' && magic[2] == 0x03 && magic[3] == 0x04)
  176. {
  177. unz_global_info info;
  178. file = (FILE *) unzOpen (filename);
  179. unzGetGlobalInfo (file, &info);
  180. unzClose (file);
  181. return info.number_entry;
  182. }
  183. else
  184. return -1;
  185. }
  186. int
  187. unzip_goto_file (unzFile file, int file_index)
  188. {
  189. int retval = unzGoToFirstFile (file), n = 0;
  190. if (file_index > 0)
  191. while (n < file_index)
  192. {
  193. retval = unzGoToNextFile (file);
  194. n++;
  195. }
  196. return retval;
  197. }
  198. static int
  199. unzip_seek_helper (FILE *file, int offset)
  200. {
  201. char buffer[MAXBUFSIZE];
  202. int n, tmp, pos = unztell (file); // returns ftell() of the "current file"
  203. if (pos == offset)
  204. return 0;
  205. else if (pos > offset)
  206. {
  207. unzCloseCurrentFile (file);
  208. unzip_goto_file (file, unzip_current_file_nr);
  209. unzOpenCurrentFile (file);
  210. pos = 0;
  211. }
  212. n = offset - pos;
  213. while (n > 0 && !unzeof (file))
  214. {
  215. tmp = unzReadCurrentFile (file, buffer, n > MAXBUFSIZE ? MAXBUFSIZE : n);
  216. if (tmp < 0)
  217. return -1;
  218. n -= tmp;
  219. }
  220. return n > 0 ? -1 : 0;
  221. }
  222. FILE *
  223. fopen2 (const char *filename, const char *mode)
  224. {
  225. #undef fopen
  226. int n, len = strlen (mode), read = 0, compressed = 0;
  227. fmode2_t fmode = FM_UNDEF;
  228. st_finfo_t *finfo;
  229. FILE *file = NULL;
  230. // printf ("opening %s", filename);
  231. if (fh_map == NULL)
  232. init_fh_map ();
  233. for (n = 0; n < len; n++)
  234. {
  235. switch (mode[n])
  236. {
  237. case 'r':
  238. read = 1;
  239. break;
  240. case 'f':
  241. case 'h':
  242. case '1':
  243. case '2':
  244. case '3':
  245. case '4':
  246. case '5':
  247. case '6':
  248. case '7':
  249. case '8':
  250. case '9':
  251. fmode = FM_GZIP;
  252. break;
  253. case 'w':
  254. case 'a':
  255. fmode = FM_NORMAL;
  256. break;
  257. case '+':
  258. if (fmode == FM_UNDEF)
  259. fmode = FM_NORMAL;
  260. break;
  261. }
  262. }
  263. if (read)
  264. {
  265. #undef fread
  266. #undef fclose
  267. unsigned char magic[4] = { 0 };
  268. FILE *fh;
  269. // TODO?: check if mode is valid for fopen(), i.e., no 'f', 'h' or number
  270. if ((fh = fopen (filename, mode)) != NULL)
  271. {
  272. fread (magic, sizeof (magic), 1, fh);
  273. if (magic[0] == 0x1f && magic[1] == 0x8b && magic[2] == 0x08)
  274. { // ID1, ID2 and CM. gzip uses Compression Method 8
  275. fmode = FM_GZIP;
  276. compressed = 1;
  277. }
  278. else if (magic[0] == 'P' && magic[1] == 'K' &&
  279. magic[2] == 0x03 && magic[3] == 0x04)
  280. {
  281. fmode = FM_ZIP;
  282. compressed = 1;
  283. }
  284. else
  285. /*
  286. Files that are opened with mode "r+" will probably be written to.
  287. zlib doesn't support mode "r+", so we have to use FM_NORMAL.
  288. Mode "r" doesn't require FM_NORMAL and FM_GZIP works, but we
  289. shouldn't introduce needless overhead.
  290. */
  291. fmode = FM_NORMAL;
  292. fclose (fh);
  293. }
  294. #define fread fread2
  295. #define fclose fclose2
  296. }
  297. if (fmode == FM_NORMAL)
  298. file = fopen (filename, mode);
  299. else if (fmode == FM_GZIP)
  300. file = (FILE *) gzopen (filename, mode);
  301. else if (fmode == FM_ZIP)
  302. {
  303. file = (FILE *) unzOpen (filename);
  304. if (file != NULL)
  305. {
  306. unzip_goto_file (file, unzip_current_file_nr);
  307. unzOpenCurrentFile (file);
  308. }
  309. }
  310. if (file == NULL)
  311. return NULL;
  312. finfo = &finfo_list[fmode * 2 + compressed];
  313. fh_map = map_put (fh_map, file, finfo);
  314. /*
  315. printf (", ptr = %p, mode = %s, fmode = %s\n", file, mode,
  316. fmode == FM_NORMAL ? "FM_NORMAL" :
  317. (fmode == FM_GZIP ? "FM_GZIP" :
  318. (fmode == FM_ZIP ? "FM_ZIP" : "FM_UNDEF")));
  319. map_dump (fh_map);
  320. */
  321. return file;
  322. #define fopen fopen2
  323. }
  324. int
  325. fclose2 (FILE *file)
  326. {
  327. #undef fclose
  328. fmode2_t fmode = get_fmode (file);
  329. map_del (fh_map, file);
  330. if (fmode == FM_NORMAL)
  331. return fclose (file);
  332. else if (fmode == FM_GZIP)
  333. return gzclose ((gzFile) file);
  334. else if (fmode == FM_ZIP)
  335. {
  336. unzCloseCurrentFile (file);
  337. return unzClose (file);
  338. }
  339. else
  340. return EOF;
  341. #define fclose fclose2
  342. }
  343. int
  344. fseek2 (FILE *file, long offset, int mode)
  345. {
  346. #undef fseek
  347. st_finfo_t *finfo = get_finfo (file);
  348. /*
  349. // if (fmode != FM_NORMAL)
  350. printf ("fmode = %s\n", finfo->fmode == FM_NORMAL ? "FM_NORMAL" :
  351. (finfo->fmode == FM_GZIP ? "FM_GZIP" :
  352. (finfo->fmode == FM_ZIP ? "FM_ZIP" : "FM_UNDEF")));
  353. */
  354. if (finfo->fmode == FM_NORMAL)
  355. return fseek (file, offset, mode);
  356. else if (finfo->fmode == FM_GZIP)
  357. {
  358. if (mode == SEEK_END) // zlib doesn't support SEEK_END
  359. {
  360. // Note that this is _slow_...
  361. unsigned char buf[MAXBUFSIZE];
  362. while (gzread ((gzFile) file, buf, MAXBUFSIZE) > 0)
  363. ;
  364. offset += gztell ((gzFile) file);
  365. mode = SEEK_SET;
  366. }
  367. return gzseek ((gzFile) file, offset, mode) == -1 ? -1 : 0;
  368. }
  369. else if (finfo->fmode == FM_ZIP)
  370. {
  371. int base;
  372. if (mode != SEEK_SET && mode != SEEK_CUR && mode != SEEK_END)
  373. {
  374. errno = EINVAL;
  375. return -1;
  376. }
  377. if (mode == SEEK_SET)
  378. base = 0;
  379. else if (mode == SEEK_CUR)
  380. base = unztell (file);
  381. else // mode == SEEK_END
  382. {
  383. unz_file_info info;
  384. unzip_goto_file (file, unzip_current_file_nr);
  385. unzGetCurrentFileInfo (file, &info, NULL, 0, NULL, 0, NULL, 0);
  386. base = info.uncompressed_size;
  387. }
  388. return unzip_seek_helper (file, base + offset);
  389. }
  390. return -1;
  391. #define fseek fseek2
  392. }
  393. size_t
  394. fread2 (void *buffer, size_t size, size_t number, FILE *file)
  395. {
  396. #undef fread
  397. fmode2_t fmode = get_fmode (file);
  398. if (size == 0 || number == 0)
  399. return 0;
  400. if (fmode == FM_NORMAL)
  401. return fread (buffer, size, number, file);
  402. else if (fmode == FM_GZIP)
  403. {
  404. int n = gzread ((gzFile) file, buffer, number * size);
  405. return n / size;
  406. }
  407. else if (fmode == FM_ZIP)
  408. {
  409. int n = unzReadCurrentFile (file, buffer, number * size);
  410. return n / size;
  411. }
  412. return 0;
  413. #define fread fread2
  414. }
  415. int
  416. fgetc2 (FILE *file)
  417. {
  418. #undef fgetc
  419. fmode2_t fmode = get_fmode (file);
  420. if (fmode == FM_NORMAL)
  421. return fgetc (file);
  422. else if (fmode == FM_GZIP)
  423. return gzgetc ((gzFile) file);
  424. else if (fmode == FM_ZIP)
  425. {
  426. char c;
  427. int retval = unzReadCurrentFile (file, &c, 1);
  428. return retval <= 0 ? EOF : c & 0xff; // avoid sign bit extension
  429. }
  430. else
  431. return EOF;
  432. #define fgetc fgetc2
  433. }
  434. char *
  435. fgets2 (char *buffer, int maxlength, FILE *file)
  436. {
  437. #undef fgets
  438. fmode2_t fmode = get_fmode (file);
  439. if (fmode == FM_NORMAL)
  440. return fgets (buffer, maxlength, file);
  441. else if (fmode == FM_GZIP)
  442. {
  443. char *retval = gzgets ((gzFile) file, buffer, maxlength);
  444. return retval == Z_NULL ? NULL : retval;
  445. }
  446. else if (fmode == FM_ZIP)
  447. {
  448. int n = 0, c = 0;
  449. while (n < maxlength - 1 && (c = fgetc (file)) != EOF)
  450. {
  451. buffer[n] = (char) c; // '\n' must also be stored in buffer
  452. n++;
  453. if (c == '\n')
  454. {
  455. buffer[n] = 0;
  456. break;
  457. }
  458. }
  459. if (n >= maxlength - 1 || c == EOF)
  460. buffer[n] = 0;
  461. return n > 0 ? buffer : NULL;
  462. }
  463. else
  464. return NULL;
  465. #define fgets fgets2
  466. }
  467. int
  468. feof2 (FILE *file)
  469. {
  470. #undef feof
  471. fmode2_t fmode = get_fmode (file);
  472. if (fmode == FM_NORMAL)
  473. return feof (file);
  474. else if (fmode == FM_GZIP)
  475. return gzeof ((gzFile) file);
  476. else if (fmode == FM_ZIP)
  477. return unzeof (file); // returns feof() of the "current file"
  478. else
  479. return -1;
  480. #define feof feof2
  481. }
  482. size_t
  483. fwrite2 (const void *buffer, size_t size, size_t number, FILE *file)
  484. {
  485. #undef fwrite
  486. fmode2_t fmode = get_fmode (file);
  487. if (size == 0 || number == 0)
  488. return 0;
  489. if (fmode == FM_NORMAL)
  490. return fwrite (buffer, size, number, file);
  491. else if (fmode == FM_GZIP)
  492. {
  493. int n = gzwrite ((gzFile) file, (void *) buffer, number * size);
  494. return n / size;
  495. }
  496. else
  497. return 0; // writing to zip files is not supported
  498. #define fwrite fwrite2
  499. }
  500. int
  501. fputc2 (int character, FILE *file)
  502. {
  503. #undef fputc
  504. fmode2_t fmode = get_fmode (file);
  505. if (fmode == FM_NORMAL)
  506. return fputc (character, file);
  507. else if (fmode == FM_GZIP)
  508. return gzputc ((gzFile) file, character);
  509. else
  510. return EOF; // writing to zip files is not supported
  511. #define fputc fputc2
  512. }
  513. long
  514. ftell2 (FILE *file)
  515. {
  516. #undef ftell
  517. fmode2_t fmode = get_fmode (file);
  518. if (fmode == FM_NORMAL)
  519. return ftell (file);
  520. else if (fmode == FM_GZIP)
  521. return gztell ((gzFile) file);
  522. else if (fmode == FM_ZIP)
  523. return unztell (file); // returns ftell() of the "current file"
  524. else
  525. return -1;
  526. #define ftell ftell2
  527. }
  528. void
  529. rewind2 (FILE *file)
  530. {
  531. fseek2 (file, 0, SEEK_SET);
  532. }
  533. FILE *
  534. popen2 (const char *command, const char *mode)
  535. {
  536. #undef popen
  537. #if defined _WIN32 || defined AMIGA
  538. #define popen _popen
  539. #endif
  540. int compressed = 0;
  541. fmode2_t fmode = FM_NORMAL;
  542. st_finfo_t *finfo;
  543. FILE *file;
  544. if (fh_map == NULL)
  545. init_fh_map ();
  546. if ((file = popen (command, mode)) == NULL)
  547. return NULL;
  548. finfo = &finfo_list[fmode * 2 + compressed];
  549. fh_map = map_put (fh_map, file, finfo);
  550. return file;
  551. #undef popen
  552. #define popen popen2
  553. }
  554. int
  555. pclose2 (FILE *stream)
  556. {
  557. #undef pclose
  558. #if defined _WIN32 || defined AMIGA
  559. #define pclose _pclose
  560. #endif
  561. fmode2_t fmode = get_fmode (stream);
  562. if (fmode == FM_NORMAL)
  563. return pclose (stream);
  564. else if (fmode == FM_GZIP || fmode == FM_ZIP)
  565. return -1;
  566. else
  567. return -1;
  568. #undef pclose
  569. #define pclose pclose2
  570. }
  571. #endif // USE_ZLIB