You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

375 lines
8.5 KiB

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