Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

373 řádky
8.5 KiB

  1. /*
  2. * libcaca Colour ASCII-Art library
  3. * Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net>
  4. * All Rights Reserved
  5. *
  6. * This library is free software. It comes without any warranty, to
  7. * the extent permitted by applicable law. You can redistribute it
  8. * and/or modify it under the terms of the Do What The Fuck You Want
  9. * To Public License, Version 2, as published by Sam Hocevar. See
  10. * http://sam.zoy.org/wtfpl/COPYING for more details.
  11. */
  12. /*
  13. * This file contains functions for compressed file I/O.
  14. */
  15. #include "config.h"
  16. #if !defined __KERNEL__
  17. # include <stdio.h>
  18. # include <stdlib.h>
  19. # include <string.h>
  20. # if defined HAVE_ZLIB_H
  21. # include <zlib.h>
  22. # define READSIZE 128 /* Read buffer size */
  23. # define WRITESIZE 128 /* Inflate buffer size */
  24. # endif
  25. #endif
  26. #include "caca.h"
  27. #include "caca_internals.h"
  28. #if !defined __KERNEL__ && defined HAVE_ZLIB_H
  29. static int zipread(caca_file_t *, void *, unsigned int);
  30. #endif
  31. #if !defined __KERNEL__
  32. struct caca_file
  33. {
  34. # if defined HAVE_ZLIB_H
  35. uint8_t read_buffer[READSIZE];
  36. z_stream stream;
  37. gzFile gz;
  38. int eof, zip, total;
  39. # endif
  40. FILE *f;
  41. int readonly;
  42. };
  43. #endif
  44. /** \brief Open a file for reading or writing
  45. *
  46. * Create a caca file handle for a file. If the file is zipped, it is
  47. * decompressed on the fly.
  48. *
  49. * If an error occurs, NULL is returned and \b errno is set accordingly:
  50. * - \c ENOSTS Function not implemented.
  51. * - \c EINVAL File not found or permission denied.
  52. *
  53. * \param path The file path
  54. * \param mode The file open mode
  55. * \return A file handle to \e path.
  56. */
  57. caca_file_t *caca_file_open(char const *path, const char *mode)
  58. {
  59. #if defined __KERNEL__
  60. seterrno(ENOSYS);
  61. return NULL;
  62. #else
  63. caca_file_t *fp = malloc(sizeof(*fp));
  64. fp->readonly = !!strchr(mode, 'r');
  65. # if defined HAVE_ZLIB_H
  66. uint8_t buf[4];
  67. unsigned int skip_size = 0;
  68. fp->gz = gzopen(path, fp->readonly ? "rb" : "wb");
  69. if(!fp->gz)
  70. {
  71. free(fp);
  72. seterrno(EINVAL);
  73. return NULL;
  74. }
  75. fp->eof = 0;
  76. fp->zip = 0;
  77. fp->total = 0;
  78. if(fp->readonly)
  79. {
  80. /* Parse ZIP file and go to start of first file */
  81. gzread(fp->gz, buf, 4);
  82. if(memcmp(buf, "PK\3\4", 4))
  83. {
  84. gzseek(fp->gz, 0, SEEK_SET);
  85. return fp;
  86. }
  87. fp->zip = 1;
  88. gzseek(fp->gz, 22, SEEK_CUR);
  89. gzread(fp->gz, buf, 2); /* Filename size */
  90. skip_size += (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
  91. gzread(fp->gz, buf, 2); /* Extra field size */
  92. skip_size += (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
  93. gzseek(fp->gz, skip_size, SEEK_CUR);
  94. /* Initialise inflate stream */
  95. fp->stream.total_out = 0;
  96. fp->stream.zalloc = NULL;
  97. fp->stream.zfree = NULL;
  98. fp->stream.opaque = NULL;
  99. fp->stream.next_in = NULL;
  100. fp->stream.avail_in = 0;
  101. if(inflateInit2(&fp->stream, -MAX_WBITS))
  102. {
  103. free(fp);
  104. gzclose(fp->gz);
  105. seterrno(EINVAL);
  106. return NULL;
  107. }
  108. }
  109. # else
  110. fp->f = fopen(path, mode);
  111. if(!fp->f)
  112. {
  113. free(fp);
  114. seterrno(EINVAL);
  115. return NULL;
  116. }
  117. # endif
  118. return fp;
  119. #endif
  120. }
  121. /** \brief Close a file handle
  122. *
  123. * Close and destroy the resources associated with a caca file handle.
  124. *
  125. * This function is a wrapper for fclose() or, if available, gzclose().
  126. *
  127. * \param fp The file handle
  128. * \return The return value of fclose() or gzclose().
  129. */
  130. int caca_file_close(caca_file_t *fp)
  131. {
  132. #if defined __KERNEL__
  133. seterrno(ENOSYS);
  134. return 0;
  135. #elif defined HAVE_ZLIB_H
  136. gzFile gz = fp->gz;
  137. if(fp->zip)
  138. inflateEnd(&fp->stream);
  139. free(fp);
  140. return gzclose(gz);
  141. #else
  142. FILE *f = fp->f;
  143. free(fp);
  144. return fclose(f);
  145. #endif
  146. }
  147. /** \brief Return the position in a file handle
  148. *
  149. * Return the file handle position, in bytes.
  150. *
  151. * \param fp The file handle
  152. * \return The current offset in the file handle.
  153. */
  154. uint64_t caca_file_tell(caca_file_t *fp)
  155. {
  156. #if defined __KERNEL__
  157. seterrno(ENOSYS);
  158. return 0;
  159. #elif defined HAVE_ZLIB_H
  160. if(fp->zip)
  161. return fp->total;
  162. return gztell(fp->gz);
  163. #else
  164. return ftell(fp->f);
  165. #endif
  166. }
  167. /** \brief Read data from a file handle
  168. *
  169. * Read data from a file handle and copy them into the given buffer.
  170. *
  171. * \param fp The file handle
  172. * \param ptr The destination buffer
  173. * \param size The number of bytes to read
  174. * \return The number of bytes read
  175. */
  176. size_t caca_file_read(caca_file_t *fp, void *ptr, size_t size)
  177. {
  178. #if defined __KERNEL__
  179. seterrno(ENOSYS);
  180. return 0;
  181. #elif defined HAVE_ZLIB_H
  182. if(fp->zip)
  183. return zipread(fp, ptr, size);
  184. return gzread(fp->gz, ptr, size);
  185. #else
  186. return fread(ptr, 1, size, fp->f);
  187. #endif
  188. }
  189. /** \brief Write data to a file handle
  190. *
  191. * Write the contents of the given buffer to the file handle.
  192. *
  193. * \param fp The file handle
  194. * \param ptr The source buffer
  195. * \param size The number of bytes to write
  196. * \return The number of bytes written
  197. */
  198. size_t caca_file_write(caca_file_t *fp, const void *ptr, size_t size)
  199. {
  200. #if defined __KERNEL__
  201. seterrno(ENOSYS);
  202. return 0;
  203. #else
  204. if(fp->readonly)
  205. return 0;
  206. # if defined HAVE_ZLIB_H
  207. if(fp->zip)
  208. {
  209. /* FIXME: zip files are not supported */
  210. seterrno(ENOSYS);
  211. return 0;
  212. }
  213. return gzwrite(fp->gz, ptr, size);
  214. # else
  215. return fwrite(ptr, 1, size, fp->f);
  216. # endif
  217. #endif
  218. }
  219. /** \brief Read a line from a file handle
  220. *
  221. * Read one line of data from a file handle, up to one less than the given
  222. * number of bytes. A trailing zero is appended to the data.
  223. *
  224. * \param fp The file handle
  225. * \param s The destination buffer
  226. * \param size The maximum number of bytes to read
  227. * \return The number of bytes read, including the trailing zero
  228. */
  229. char *caca_file_gets(caca_file_t *fp, char *s, int size)
  230. {
  231. #if defined __KERNEL__
  232. seterrno(ENOSYS);
  233. return NULL;
  234. #elif defined HAVE_ZLIB_H
  235. if(fp->zip)
  236. {
  237. int i;
  238. for(i = 0; i < size; i++)
  239. {
  240. int ret = zipread(fp, s + i, 1);
  241. if(ret < 0)
  242. return NULL;
  243. if(ret == 0 || s[i] == '\n')
  244. {
  245. if(i + 1 < size)
  246. s[i + 1] = '\0';
  247. return s;
  248. }
  249. }
  250. return s;
  251. }
  252. return gzgets(fp->gz, s, size);
  253. #else
  254. return fgets(s, size, fp->f);
  255. #endif
  256. }
  257. /** \brief Tell whether a file handle reached end of file
  258. *
  259. * Return the end-of-file status of the file handle.
  260. *
  261. * This function is a wrapper for feof() or, if available, gzeof().
  262. *
  263. * \param fp The file handle
  264. * \return 1 if EOF was reached, 0 otherwise
  265. */
  266. int caca_file_eof(caca_file_t *fp)
  267. {
  268. #if defined __KERNEL__
  269. return 1;
  270. #elif defined HAVE_ZLIB_H
  271. return fp->zip ? fp->eof : gzeof(fp->gz);
  272. #else
  273. return feof(fp->f);
  274. #endif
  275. }
  276. #if !defined __KERNEL__ && defined HAVE_ZLIB_H
  277. static int zipread(caca_file_t *fp, void *buf, unsigned int len)
  278. {
  279. unsigned int total_read = 0;
  280. if(len == 0)
  281. return 0;
  282. fp->stream.next_out = buf;
  283. fp->stream.avail_out = len;
  284. while(fp->stream.avail_out > 0)
  285. {
  286. unsigned int tmp;
  287. int ret = 0;
  288. if(fp->stream.avail_in == 0 && !gzeof(fp->gz))
  289. {
  290. int bytes_read;
  291. bytes_read = gzread(fp->gz, fp->read_buffer, READSIZE);
  292. if(bytes_read < 0)
  293. return -1;
  294. fp->stream.next_in = fp->read_buffer;
  295. fp->stream.avail_in = bytes_read;
  296. }
  297. tmp = fp->stream.total_out;
  298. ret = inflate(&fp->stream, Z_SYNC_FLUSH);
  299. total_read += fp->stream.total_out - tmp;
  300. if(ret == Z_STREAM_END)
  301. {
  302. fp->eof = 1;
  303. fp->total += total_read;
  304. return total_read;
  305. }
  306. if(ret != Z_OK)
  307. return ret;
  308. }
  309. fp->total += total_read;
  310. return total_read;
  311. }
  312. #endif
  313. /*
  314. * XXX: The following functions are aliases.
  315. */
  316. cucul_file_t *cucul_file_open(char const *, const char *)
  317. CACA_ALIAS(caca_file_open);
  318. int cucul_file_close(cucul_file_t *) CACA_ALIAS(caca_file_close);
  319. uint64_t cucul_file_tell(cucul_file_t *) CACA_ALIAS(caca_file_tell);
  320. size_t cucul_file_read(cucul_file_t *, void *, size_t)
  321. CACA_ALIAS(caca_file_read);
  322. size_t cucul_file_write(cucul_file_t *, const void *, size_t)
  323. CACA_ALIAS(caca_file_write);
  324. char * cucul_file_gets(cucul_file_t *, char *, int)
  325. CACA_ALIAS(caca_file_gets);
  326. int cucul_file_eof(cucul_file_t *) CACA_ALIAS(caca_file_eof);