Make extended attribute support optional.
[shigofumi.git] / src / io.c
blob154bd0759ec2eca311707fdabaa4a549f8ffdd75
1 #define _XOPEN_SOURCE 500
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <stdio.h>
6 #include <unistd.h>
7 #include <string.h>
8 #include <errno.h>
9 #include <sys/mman.h>
10 #include <stdint.h>
11 #include <magic.h>
12 #include <limits.h> /* SSIZE_MAX */
14 #include "shigofumi.h"
16 #if ENABLE_XATTR
17 #include <attr/xattr.h>
18 #endif
21 /* Guess MIME type of @file by content.
22 * @type is automatically allocated. Return 0, -1 and * NULL @type in case
23 * of error */
24 static int get_mime_type_by_magic(const char *file, char **type) {
25 magic_t magic;
26 const char *magic_guess;
28 if (!type) return -1;
29 *type = NULL;
30 if (!file || !*file) return -1;
32 if (!(magic = magic_open(MAGIC_MIME_TYPE))) {
33 fprintf(stderr, _("Could not initialize libmagic\n"));
34 return -1;
36 if (magic_load(magic, NULL)) {
37 fprintf(stderr, _("Could not load magic database\n"));
38 return -1;
41 if (!(magic_guess = magic_file(magic, file))) {
42 fprintf(stderr, _("%s: Could not guess MIME type: %s\n"),
43 file, magic_error(magic));
44 magic_close(magic);
45 return -1;
48 if (!(*type = strdup(magic_guess))) {
49 fprintf(stderr, _("Not enough memory\n"));
50 magic_close(magic);
51 return -1;
54 magic_close(magic);
55 return 0;
59 #if ENABLE_XATTR
60 #define XATTR_MIME_TYPE "user.mime_type"
62 /* Get MIME type from file extended attribute.
63 * @fd is valid file desciptor or -1
64 * @type is automatically allocated. Return 0, -1 and * NULL @type in case
65 * of error */
66 /* XXX: This is Linux specific. There is a library, but it supports IRIX
67 * in addition only. */
68 static int get_mime_type_by_xattr(int fd, char **type) {
69 char *buffer = NULL;
70 ssize_t length = 0, ret;
72 if (!type) return -1;
73 *type = NULL;
74 if (fd < 0) return -1;
76 /* Get inital size */
77 length = fgetxattr(fd, XATTR_MIME_TYPE, NULL, 0);
78 if (length <= 0) return -1;
80 /* Grow buffer as needed */
81 for (ret = -1, errno = ERANGE; ret < 0 && errno == ERANGE; length *= 2) {
82 buffer = realloc(*type, length + 1);
83 if (!buffer) {
84 fprintf(stderr, _("Not enough memory\n"));
85 zfree(*type);
86 return -1;
88 *type = buffer;
89 ret = fgetxattr(fd, XATTR_MIME_TYPE, *type, length);
91 /* Break on length overflow */
92 if (length >= SSIZE_MAX / 2) {
93 fprintf(stderr, _("Extended attribute too long\n"));
94 zfree(*type);
95 return -1;
99 if (ret <= 0) {
100 zfree(*type);
101 return -1;
104 (*type)[ret] = '\0';
105 return 0;
107 #endif /* ENABLE_XATTR */
110 /* Get MIME type of file.
111 * File can be specified by name or descriptor or both.
112 * @fd is open file descriptor of the file or -1
113 * @name is name of the same file or NULL
114 * @type is automatically allocated.
115 * Return 0; -1 and NULL @type in case of error */
116 static int get_mime_type(int fd, const char *name, char **type) {
118 if (!type) return -1;
119 *type = NULL;
120 if ((!name || !*name) && fd == -1) return -1;
122 #ifdef ENABLE_XATTR
123 if (get_mime_type_by_xattr(fd, type))
124 #endif
125 if (get_mime_type_by_magic(name, type))
126 return -1;
128 return 0;
132 /* Annotate MIME type to a file
133 * @fd is descriptor of the file
134 * @type is mime_type in UTF-8
135 * Return 0 on success, -1 on failure */
136 /* XXX: Linux specific */
137 static int save_mime_type(int fd, const char *type) {
138 if (!type || !*type) return -1;
140 #if ENABLE_XATTR
141 if (fsetxattr(fd, XATTR_MIME_TYPE, type, strlen(type), 0)) {
142 fprintf(stderr,
143 _("Could not save MIME type into extended attribute: %s\n"),
144 strerror(errno));
145 return -1;
147 #endif /* ENABLE_XATTR */
148 return 0;
151 #undef XATTR_MIME_TYPE
154 /* Open file and return descriptor. Return -1 in case of error. */
155 int open_file_for_writing(const char *file, _Bool truncate) {
156 int fd;
158 if (!file) return -1;
160 fd = open(file, O_WRONLY|O_CREAT|O_APPEND| ((truncate) ? O_TRUNC : 0),
161 0666);
162 if (fd == -1) {
163 fprintf(stderr, _("%s: Could not open file for writing: %s\n"),
164 file, strerror(errno));
167 return fd;
171 /* Return 0, -1 in case of error */
172 int mmap_file(const char *file, int *fd, void **buffer, size_t *length) {
173 struct stat file_info;
175 if (!file || !fd || !buffer || !length) return -1;
178 *fd = open(file, O_RDONLY);
179 if (*fd == -1) {
180 fprintf(stderr, _("%s: Could not open file: %s\n"),
181 file, strerror(errno));
182 return -1;
185 if (-1 == fstat(*fd, &file_info)) {
186 fprintf(stderr, _("%s: Could not get file size: %s\n"), file,
187 strerror(errno));
188 close(*fd);
189 return -1;
191 if (file_info.st_size < 0) {
192 fprintf(stderr, _("File `%s' has negative size: %jd\n"), file,
193 (intmax_t) file_info.st_size);
194 close(*fd);
195 return -1;
197 *length = file_info.st_size;
199 *buffer = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, *fd, 0);
200 if (*buffer == MAP_FAILED) {
201 fprintf(stderr, _("%s: Could not map file to memory: %s\n"), file,
202 strerror(errno));
203 close(*fd);
204 return -1;
207 return 0;
211 /* Return 0, -1 in case of error */
212 int munmap_file(int fd, void *buffer, size_t length) {
213 int err = 0;
214 long int page_size = sysconf(_SC_PAGE_SIZE);
215 size_t pages = (length % page_size) ?
216 ((length / page_size) + 1) * page_size:
217 length;
219 err = munmap(buffer, pages);
220 if (err) {
221 fprintf(stderr, _("Could not unmap memory at %p and length %zu: %s\n"),
222 buffer, pages, strerror(errno));
225 err = close(fd);
226 if (err) {
227 fprintf(stderr, _("Could close file descriptor %d: %s\n"), fd,
228 strerror(errno));
231 return err;
235 /* Return 0, -1 in case of error.
236 * @length and @mime_type are optional. */
237 int load_data_from_file(const char *file, void **data, size_t *length,
238 char **mime_type) {
239 int fd;
240 void *buffer;
241 size_t map_length;
243 if (!file || !data) return -1;
245 if (mmap_file(file, &fd, &buffer, &map_length)) return -1;
247 printf(ngettext("Reading %zu byte from file `%s'...\n",
248 "Reading %zu bytes from file `%s'...\n", map_length),
249 map_length, file);
250 *data = malloc(map_length);
251 if (!*data) {
252 fprintf(stderr, _("Error: Not enough memory\n"));
253 munmap_file(fd, buffer, map_length);
254 return -1;
256 memcpy(*data, buffer, map_length);
257 if (length) *length = map_length;
259 if (mime_type) {
260 if (get_mime_type(fd, file, mime_type))
261 fprintf(stderr, _("Warning: %s: Could not determine MIME type\n"),
262 file);
264 } else {
265 *mime_type = NULL;
268 munmap_file(fd, buffer, map_length);
269 printf(_("Done.\n"));
270 return 0;
274 int save_data_to_file(const char *file, const void *data,
275 const size_t length, const char *mime_type) {
276 int fd;
277 ssize_t written, left = length;
279 if (!file) return -1;
280 if (length > 0 && !data) return -1;
282 fd = open_file_for_writing(file, 1);
283 if (fd == -1) return -1;
285 printf(ngettext("Writing %zu byte to file `%s'...\n",
286 "Writing %zu bytes to file `%s'...\n", length), length, file);
287 while (left) {
288 written = write(fd, data + length - left, left);
289 if (written == -1) {
290 fprintf(stderr, _("%s: Could not save file: %s\n"),
291 file, strerror(errno));
292 close(fd);
293 return -1;
295 left-=written;
298 save_mime_type(fd, mime_type);
300 if (-1 == close(fd)) {
301 fprintf(stderr, _("%s: Closing file failed: %s\n"),
302 file, strerror(errno));
303 return -1;
306 printf(_("Done.\n"));
307 return 0;
311 /* Return 0 if @path is directory, 1 if not, -1 if error occured */
312 int is_directory(const char *path) {
313 struct stat dir_stat;
315 if (!path) return -1;
316 if (stat(path, &dir_stat)) return -1;
317 if S_ISDIR(dir_stat.st_mode) return 0;
318 else return 1;