Correct typos
[shigofumi.git] / src / io.c
blob5b83c7475293d22de479c3eefb8c52a187da7657
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 */
13 #include <isds.h>
14 #include <libxml/xmlsave.h> /* For XML serializator */
15 #include <libxml/xpath.h> /* For XPath */
17 #include "shigofumi.h"
19 #if ENABLE_XATTR
20 #include <attr/xattr.h>
21 #endif
24 /* Guess MIME type of @file by content.
25 * @type is automatically allocated. Return 0, -1 and * NULL @type in case
26 * of error */
27 static int get_mime_type_by_magic(const char *file, char **type) {
28 magic_t magic;
29 const char *magic_guess;
31 if (!type) return -1;
32 *type = NULL;
33 if (!file || !*file) return -1;
35 if (!(magic = magic_open(MAGIC_MIME_TYPE))) {
36 fprintf(stderr, _("Could not initialize libmagic\n"));
37 return -1;
39 if (magic_load(magic, NULL)) {
40 fprintf(stderr, _("Could not load magic database\n"));
41 return -1;
44 if (!(magic_guess = magic_file(magic, file))) {
45 fprintf(stderr, _("%s: Could not guess MIME type: %s\n"),
46 file, magic_error(magic));
47 magic_close(magic);
48 return -1;
51 if (!(*type = strdup(magic_guess))) {
52 fprintf(stderr, _("Not enough memory\n"));
53 magic_close(magic);
54 return -1;
57 magic_close(magic);
58 return 0;
62 #if ENABLE_XATTR
63 #define XATTR_MIME_TYPE "user.mime_type"
65 /* Get MIME type from file extended attribute.
66 * @fd is valid file descriptor or -1
67 * @type is automatically allocated. Return 0, -1 and * NULL @type in case
68 * of error */
69 /* XXX: This is Linux specific. There is a library, but it supports IRIX
70 * in addition only. */
71 static int get_mime_type_by_xattr(int fd, char **type) {
72 char *buffer = NULL;
73 ssize_t length = 0, ret;
75 if (!type) return -1;
76 *type = NULL;
77 if (fd < 0) return -1;
79 /* Get initial size */
80 length = fgetxattr(fd, XATTR_MIME_TYPE, NULL, 0);
81 if (length <= 0) return -1;
83 /* Grow buffer as needed */
84 for (ret = -1, errno = ERANGE; ret < 0 && errno == ERANGE; length *= 2) {
85 buffer = realloc(*type, length + 1);
86 if (!buffer) {
87 fprintf(stderr, _("Not enough memory\n"));
88 zfree(*type);
89 return -1;
91 *type = buffer;
92 ret = fgetxattr(fd, XATTR_MIME_TYPE, *type, length);
94 /* Break on length overflow */
95 if (length >= SSIZE_MAX / 2) {
96 fprintf(stderr, _("Extended attribute too long\n"));
97 zfree(*type);
98 return -1;
102 if (ret <= 0) {
103 zfree(*type);
104 return -1;
107 (*type)[ret] = '\0';
108 return 0;
110 #endif /* ENABLE_XATTR */
113 /* Get MIME type of file.
114 * File can be specified by name or descriptor or both.
115 * @fd is open file descriptor of the file or -1
116 * @name is name of the same file or NULL
117 * @type is automatically allocated.
118 * Return 0; -1 and NULL @type in case of error */
119 static int get_mime_type(int fd, const char *name, char **type) {
121 if (!type) return -1;
122 *type = NULL;
123 if ((!name || !*name) && fd == -1) return -1;
125 #ifdef ENABLE_XATTR
126 if (get_mime_type_by_xattr(fd, type))
127 #endif
128 if (get_mime_type_by_magic(name, type))
129 return -1;
131 return 0;
135 /* Annotate MIME type to a file
136 * @fd is descriptor of the file
137 * @type is mime_type in UTF-8
138 * Return 0 on success, -1 on failure */
139 /* XXX: Linux specific */
140 static int save_mime_type(int fd, const char *type) {
141 if (!type || !*type) return -1;
143 #if ENABLE_XATTR
144 if (fsetxattr(fd, XATTR_MIME_TYPE, type, strlen(type), 0)) {
145 fprintf(stderr,
146 _("Could not save MIME type into extended attribute: %s\n"),
147 strerror(errno));
148 return -1;
150 #endif /* ENABLE_XATTR */
151 return 0;
154 #undef XATTR_MIME_TYPE
157 /* Open file and return descriptor. Return -1 in case of error. */
158 int open_file_for_writing(const char *file, _Bool truncate) {
159 int fd;
161 if (!file) return -1;
163 fd = open(file, O_WRONLY|O_CREAT|O_APPEND| ((truncate) ? O_TRUNC : 0),
164 0666);
165 if (fd == -1) {
166 fprintf(stderr, _("%s: Could not open file for writing: %s\n"),
167 file, strerror(errno));
170 return fd;
174 /* Return 0, -1 in case of error */
175 int mmap_file(const char *file, int *fd, void **buffer, size_t *length) {
176 struct stat file_info;
178 if (!file || !fd || !buffer || !length) return -1;
181 *fd = open(file, O_RDONLY);
182 if (*fd == -1) {
183 fprintf(stderr, _("%s: Could not open file: %s\n"),
184 file, strerror(errno));
185 return -1;
188 if (-1 == fstat(*fd, &file_info)) {
189 fprintf(stderr, _("%s: Could not get file size: %s\n"), file,
190 strerror(errno));
191 close(*fd);
192 return -1;
194 if (file_info.st_size < 0) {
195 fprintf(stderr, _("File `%s' has negative size: %jd\n"), file,
196 (intmax_t) file_info.st_size);
197 close(*fd);
198 return -1;
200 *length = file_info.st_size;
202 *buffer = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, *fd, 0);
203 if (*buffer == MAP_FAILED) {
204 fprintf(stderr, _("%s: Could not map file to memory: %s\n"), file,
205 strerror(errno));
206 close(*fd);
207 return -1;
210 return 0;
214 /* Return 0, -1 in case of error */
215 int munmap_file(int fd, void *buffer, size_t length) {
216 int err = 0;
217 long int page_size = sysconf(_SC_PAGE_SIZE);
218 size_t pages = (length % page_size) ?
219 ((length / page_size) + 1) * page_size:
220 length;
222 err = munmap(buffer, pages);
223 if (err) {
224 fprintf(stderr, _("Could not unmap memory at %p and length %zu: %s\n"),
225 buffer, pages, strerror(errno));
228 err = close(fd);
229 if (err) {
230 fprintf(stderr, _("Could close file descriptor %d: %s\n"), fd,
231 strerror(errno));
234 return err;
238 /* Return 0, -1 in case of error.
239 * @length and @mime_type are optional. */
240 int load_data_from_file(const char *file, void **data, size_t *length,
241 char **mime_type) {
242 int fd;
243 void *buffer;
244 size_t map_length;
246 if (!file || !data) return -1;
248 if (mmap_file(file, &fd, &buffer, &map_length)) return -1;
250 printf(ngettext("Reading %zu byte from file `%s'...\n",
251 "Reading %zu bytes from file `%s'...\n", map_length),
252 map_length, file);
253 *data = malloc(map_length);
254 if (!*data) {
255 fprintf(stderr, _("Error: Not enough memory\n"));
256 munmap_file(fd, buffer, map_length);
257 return -1;
259 memcpy(*data, buffer, map_length);
260 if (length) *length = map_length;
262 if (mime_type) {
263 if (get_mime_type(fd, file, mime_type))
264 fprintf(stderr, _("Warning: %s: Could not determine MIME type\n"),
265 file);
267 } else {
268 *mime_type = NULL;
271 munmap_file(fd, buffer, map_length);
272 printf(_("Done.\n"));
273 return 0;
277 int save_data_to_file(const char *file, const void *data,
278 const size_t length, const char *mime_type) {
279 int fd;
280 ssize_t written, left = length;
282 if (!file) return -1;
283 if (length > 0 && !data) return -1;
285 fd = open_file_for_writing(file, 1);
286 if (fd == -1) return -1;
288 printf(ngettext("Writing %zu byte to file `%s'...\n",
289 "Writing %zu bytes to file `%s'...\n", length), length, file);
290 while (left) {
291 written = write(fd, data + length - left, left);
292 if (written == -1) {
293 fprintf(stderr, _("%s: Could not save file: %s\n"),
294 file, strerror(errno));
295 close(fd);
296 return -1;
298 left-=written;
301 save_mime_type(fd, mime_type);
303 if (-1 == close(fd)) {
304 fprintf(stderr, _("%s: Closing file failed: %s\n"),
305 file, strerror(errno));
306 return -1;
309 printf(_("Done.\n"));
310 return 0;
314 /* @node_list is pointer to by-function allocated weak copy of libxml node
315 * pointers list. *NULL means empty list. It will be freed and NULLed in case
316 * of error.
317 * @xpat_expr is UTF-8 encoded XPath expression. */
318 static int xpath2nodelist(xmlNodePtr *node_list, xmlXPathContextPtr xpath_ctx,
319 const xmlChar *xpath_expr) {
320 xmlXPathObjectPtr result = NULL;
321 xmlNodePtr node = NULL, prev_node = NULL;
323 if (!node_list || !xpath_ctx || !xpath_expr) return -1;
325 result = xmlXPathEvalExpression(xpath_expr, xpath_ctx);
326 if (!result) {
327 char *xpath_expr_locale = utf82locale((const char*) xpath_expr);
328 fprintf(stderr, _("Error while evaluating XPath expression `%s'\n"),
329 xpath_expr_locale);
330 free(xpath_expr_locale);
331 return -1;
334 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
335 /* Empty match, returning empty node list */
336 *node_list = NULL;
337 } else {
338 /* Convert node set to list of siblings */
339 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
340 /* Make weak copy of the node */
341 node = malloc(sizeof(*node));
342 if (!node) {
343 fprintf(stderr, _("Not enough memory\n"));
344 xmlXPathFreeObject(result);
345 for (node = *node_list; node; node = node->next)
346 free(node);
347 *node_list = NULL;
348 return -1;
350 memcpy(node, result->nodesetval->nodeTab[i], sizeof(*node));
352 /* Add node to node_list */
353 node->prev = prev_node;
354 node->next = NULL;
355 if (prev_node)
356 prev_node->next = node;
357 else
358 *node_list = node;
359 prev_node = node;
361 /* Debug */
362 /*printf("* Embeding node #%d:\n", i);
363 xmlDebugDumpNode(stdout, node, 2);*/
367 xmlXPathFreeObject(result);
368 return 0;
372 /* Parse @buffer as XML document and return @node_list specified by
373 * @xpath_expression.
374 * @node_list is weak copy that must be non-recursively freed by caller. Caller
375 * must free XML document (accessible through @node_list member) on its own.
376 * In case of error @node_list value will be invalid.
377 * If @node_list is returned empty, function freed parsed document already.
378 * @xpat_expr is UTF-8 encoded XPath expression */
379 int load_xml_subtree_from_memory(const void *buffer, size_t length,
380 xmlNodePtr *node_list, const char *xpath_expr) {
381 int retval = 0;
382 xmlDocPtr xml = NULL;
383 xmlXPathContextPtr xpath_ctx = NULL;
385 if (!buffer || !length) {
386 fprintf(stderr,
387 _("Empty XML document, XPath expression cannot be evaluated\n"));
388 return -1;
390 if (!node_list || !xpath_expr)
391 return -1;
392 *node_list = NULL;
394 /* Create XML documents */
395 xml = xmlParseMemory(buffer, length);
396 if (!xml) {
397 fprintf(stderr, _("Error while parsing document\n"));
398 return -1;
401 xpath_ctx = xmlXPathNewContext(xml);
402 if (!xpath_ctx) {
403 fprintf(stderr, _("Error while creating XPath context\n"));
404 retval = -1;
405 goto leave;
408 if (xpath2nodelist(node_list, xpath_ctx, (const xmlChar *)xpath_expr)) {
409 char *xpath_expr_locale = utf82locale(xpath_expr);
410 fprintf(stderr, _("Could not convert XPath result to node list: %s\n"),
411 xpath_expr_locale);
412 free(xpath_expr_locale);
413 retval = -1;
414 goto leave;
417 leave:
418 xmlXPathFreeContext(xpath_ctx);
419 if (retval || !*node_list) xmlFreeDoc(xml);
420 return retval;
424 /* Deallocate struct isds_document with embedded XML recursively and NULL it */
425 void free_document_with_xml_node_list(struct isds_document **document) {
426 /* Free document xml_node_lists and associated XML document because
427 * they are weak copies and isds_document_free() does not free it. */
428 if (!document || !*document) return;
430 if ((*document)->is_xml && (*document)->xml_node_list) {
431 xmlFreeDoc((*document)->xml_node_list->doc);
432 for (xmlNodePtr node = (*document)->xml_node_list; node;
433 node = node->next)
434 free(node);
437 isds_document_free(document);
441 int save_xml_to_file(const char *file, const xmlNodePtr node_list,
442 const char *mime_type) {
443 int retval = 0;
444 xmlBufferPtr buffer = NULL;
445 xmlSaveCtxtPtr save_ctx = NULL;
446 xmlNodePtr element, subtree_copy = NULL;
447 xmlDocPtr subtree_doc = NULL;
450 /* Prepare buffer to serialize into */
451 buffer = xmlBufferCreate();
452 if (!buffer) {
453 fprintf(stderr,
454 _("Could not create buffer to serialize XML tree into"));
455 retval = -1;
456 goto leave;
458 save_ctx = xmlSaveToBuffer(buffer, "UTF-8", 0);
459 if (!save_ctx) {
460 fprintf(stderr, _("Could not create XML serializer"));
461 retval = -1;
462 goto leave;
466 /* Select node and serialize it */
467 if (!node_list->next && node_list->type == XML_TEXT_NODE) {
468 /* One text node becomes plain text file */
469 /* TODO: Is CDATA expanded as text? Are entities expanded? */
470 /* XXX: According LibXML documentation, this function does not return
471 * meaningful value yet */
472 xmlSaveTree(save_ctx, node_list);
473 } else {
474 /* Serialize element */
475 if (!node_list->next && node_list->type == XML_ELEMENT_NODE) {
476 /* One element becomes root */
477 element = node_list;
478 } else {
479 /* Parent becomes root */
480 if (!node_list->parent) {
481 fprintf(stderr,
482 _("XML node list to serialize is missing parent node"));
483 retval = -1;
484 goto leave;
486 element = node_list->parent;
489 /* Use temporary XML document to resolve name space definitions */
490 /* XXX: We can not use xmlNodeDump() nor xmlSaveTree because it dumps
491 * the subtree as is. It can result in not well-formed on invalid XML
492 * tree (e.g. name space prefix definition can miss.) */
493 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
494 if (!subtree_doc) {
495 fprintf(stderr, _("Could not build temporary XML document"));
496 retval = -1;
497 goto leave;
499 /* XXX: Copy subtree and attach the copy to document.
500 * One node can not bee attached into more document at the same time. */
501 subtree_copy = xmlCopyNodeList(element);
502 if (!subtree_copy) {
503 fprintf(stderr, _("Could not copy XML subtree"));
504 retval = -1;
505 goto leave;
507 xmlDocSetRootElement(subtree_doc, subtree_copy);
508 /* Only this way we get name space definition as @xmlns:isds,
509 * otherwise we get name space prefix without definition */
510 /* FIXME: Don't overwrite original default name space */
511 /*isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
512 if(!isds_ns) {
513 isds_log_message(context, _("Could not create ISDS name space"));
514 err = IE_ERROR;
515 goto leave;
517 xmlSetNs(subtree_copy, isds_ns);*/
519 /* XXX: According LibXML documentation, this function does not return
520 * meaningful value yet */
521 xmlSaveDoc(save_ctx, subtree_doc);
524 /* Flush XML to buffer. After this call we are sure all data have been
525 * processed successfully. */
526 if (-1 == xmlSaveFlush(save_ctx)) {
527 fprintf(stderr, _("Could not serialize XML tree"));
528 retval = -1;
529 goto leave;
532 leave:
533 xmlFreeDoc(subtree_doc); /* Frees subtree_copy */
534 xmlSaveClose(save_ctx);
536 if (!retval) {
537 /* And save buffer into file */
538 retval = save_data_to_file(file, buffer->content, buffer->use,
539 mime_type);
542 xmlBufferFree(buffer);
543 return retval;
547 /* Return 0 if @path is directory, 1 if not, -1 if error occurred */
548 int is_directory(const char *path) {
549 struct stat dir_stat;
551 if (!path) return -1;
552 if (stat(path, &dir_stat)) return -1;
553 if S_ISDIR(dir_stat.st_mode) return 0;
554 else return 1;