1 #define _XOPEN_SOURCE 500
12 #include <limits.h> /* SSIZE_MAX */
14 #include "shigofumi.h"
17 #include <attr/xattr.h>
21 /* Guess MIME type of @file by content.
22 * @type is automatically allocated. Return 0, -1 and * NULL @type in case
24 static int get_mime_type_by_magic(const char *file
, char **type
) {
26 const char *magic_guess
;
30 if (!file
|| !*file
) return -1;
32 if (!(magic
= magic_open(MAGIC_MIME_TYPE
))) {
33 fprintf(stderr
, _("Could not initialize libmagic\n"));
36 if (magic_load(magic
, NULL
)) {
37 fprintf(stderr
, _("Could not load magic database\n"));
41 if (!(magic_guess
= magic_file(magic
, file
))) {
42 fprintf(stderr
, _("%s: Could not guess MIME type: %s\n"),
43 file
, magic_error(magic
));
48 if (!(*type
= strdup(magic_guess
))) {
49 fprintf(stderr
, _("Not enough memory\n"));
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
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
) {
70 ssize_t length
= 0, ret
;
74 if (fd
< 0) return -1;
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);
84 fprintf(stderr
, _("Not enough memory\n"));
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"));
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;
120 if ((!name
|| !*name
) && fd
== -1) return -1;
123 if (get_mime_type_by_xattr(fd
, type
))
125 if (get_mime_type_by_magic(name
, type
))
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;
141 if (fsetxattr(fd
, XATTR_MIME_TYPE
, type
, strlen(type
), 0)) {
143 _("Could not save MIME type into extended attribute: %s\n"),
147 #endif /* ENABLE_XATTR */
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
) {
158 if (!file
) return -1;
160 fd
= open(file
, O_WRONLY
|O_CREAT
|O_APPEND
| ((truncate
) ? O_TRUNC
: 0),
163 fprintf(stderr
, _("%s: Could not open file for writing: %s\n"),
164 file
, strerror(errno
));
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
);
180 fprintf(stderr
, _("%s: Could not open file: %s\n"),
181 file
, strerror(errno
));
185 if (-1 == fstat(*fd
, &file_info
)) {
186 fprintf(stderr
, _("%s: Could not get file size: %s\n"), file
,
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
);
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
,
211 /* Return 0, -1 in case of error */
212 int munmap_file(int fd
, void *buffer
, size_t length
) {
214 long int page_size
= sysconf(_SC_PAGE_SIZE
);
215 size_t pages
= (length
% page_size
) ?
216 ((length
/ page_size
) + 1) * page_size
:
219 err
= munmap(buffer
, pages
);
221 fprintf(stderr
, _("Could not unmap memory at %p and length %zu: %s\n"),
222 buffer
, pages
, strerror(errno
));
227 fprintf(stderr
, _("Could close file descriptor %d: %s\n"), fd
,
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
,
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
),
250 *data
= malloc(map_length
);
252 fprintf(stderr
, _("Error: Not enough memory\n"));
253 munmap_file(fd
, buffer
, map_length
);
256 memcpy(*data
, buffer
, map_length
);
257 if (length
) *length
= map_length
;
260 if (get_mime_type(fd
, file
, mime_type
))
261 fprintf(stderr
, _("Warning: %s: Could not determine MIME type\n"),
268 munmap_file(fd
, buffer
, map_length
);
269 printf(_("Done.\n"));
274 int save_data_to_file(const char *file
, const void *data
,
275 const size_t length
, const char *mime_type
) {
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
);
288 written
= write(fd
, data
+ length
- left
, left
);
290 fprintf(stderr
, _("%s: Could not save file: %s\n"),
291 file
, strerror(errno
));
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
));
306 printf(_("Done.\n"));
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;