Add commercialcredit command
[shigofumi.git] / src / io.c
blob56eaf14f42672d9e3493432a354acbd3bbc6b990
1 #define _XOPEN_SOURCE 500
2 #define _SVID_SOURCE /* For mkstemps(3) */
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <unistd.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <sys/mman.h>
11 #include <stdint.h>
12 #include <magic.h>
13 #include <limits.h> /* SSIZE_MAX */
14 #include <stdlib.h> /* mkstemps(3) */
15 #include <isds.h>
16 #include <libxml/xmlsave.h> /* For XML serializator */
17 #include <libxml/xpath.h> /* For XPath */
19 #include "shigofumi.h"
21 #if ENABLE_XATTR
22 #include <attr/xattr.h>
23 #endif
26 /* Guess MIME type of @file by content.
27 * @type is automatically allocated. Return 0, -1 and * NULL @type in case
28 * of error */
29 static int get_mime_type_by_magic(const char *file, char **type) {
30 magic_t magic;
31 const char *magic_guess;
33 if (!type) return -1;
34 *type = NULL;
35 if (!file || !*file) return -1;
37 if (!(magic = magic_open(MAGIC_MIME_TYPE))) {
38 fprintf(stderr, _("Could not initialize libmagic\n"));
39 return -1;
41 if (magic_load(magic, NULL)) {
42 fprintf(stderr, _("Could not load magic database\n"));
43 return -1;
46 if (!(magic_guess = magic_file(magic, file))) {
47 fprintf(stderr, _("%s: Could not guess MIME type: %s\n"),
48 file, magic_error(magic));
49 magic_close(magic);
50 return -1;
53 if (!(*type = strdup(magic_guess))) {
54 fprintf(stderr, _("Not enough memory\n"));
55 magic_close(magic);
56 return -1;
59 magic_close(magic);
60 return 0;
64 #if ENABLE_XATTR
65 #define XATTR_MIME_TYPE "user.mime_type"
67 /* Get MIME type from file extended attribute.
68 * @fd is valid file descriptor or -1
69 * @type is automatically allocated. Return 0, -1 and * NULL @type in case
70 * of error */
71 /* XXX: This is Linux specific. There is a library, but it supports IRIX
72 * in addition only. */
73 static int get_mime_type_by_xattr(int fd, char **type) {
74 char *buffer = NULL;
75 ssize_t length = 0, ret;
77 if (!type) return -1;
78 *type = NULL;
79 if (fd < 0) return -1;
81 /* Get initial size */
82 length = fgetxattr(fd, XATTR_MIME_TYPE, NULL, 0);
83 if (length <= 0) return -1;
85 /* Grow buffer as needed */
86 for (ret = -1, errno = ERANGE; ret < 0 && errno == ERANGE; length *= 2) {
87 buffer = realloc(*type, length + 1);
88 if (!buffer) {
89 fprintf(stderr, _("Not enough memory\n"));
90 zfree(*type);
91 return -1;
93 *type = buffer;
94 ret = fgetxattr(fd, XATTR_MIME_TYPE, *type, length);
96 /* Break on length overflow */
97 if (length >= SSIZE_MAX / 2) {
98 fprintf(stderr, _("Extended attribute too long\n"));
99 zfree(*type);
100 return -1;
104 if (ret <= 0) {
105 zfree(*type);
106 return -1;
109 (*type)[ret] = '\0';
110 return 0;
112 #endif /* ENABLE_XATTR */
115 /* Get MIME type of file.
116 * File can be specified by name or descriptor or both.
117 * @fd is open file descriptor of the file or -1
118 * @name is name of the same file or NULL
119 * @type is automatically allocated.
120 * Return 0; -1 and NULL @type in case of error */
121 static int get_mime_type(int fd, const char *name, char **type) {
123 if (!type) return -1;
124 *type = NULL;
125 if ((!name || !*name) && fd == -1) return -1;
127 #ifdef ENABLE_XATTR
128 if (get_mime_type_by_xattr(fd, type))
129 #endif
130 if (get_mime_type_by_magic(name, type))
131 return -1;
133 return 0;
137 /* Annotate MIME type to a file
138 * @fd is descriptor of the file
139 * @type is mime_type in UTF-8
140 * Return 0 on success, -1 on failure */
141 /* XXX: Linux specific */
142 static int save_mime_type(int fd, const char *type) {
143 if (!type || !*type) return -1;
145 #if ENABLE_XATTR
146 if (fsetxattr(fd, XATTR_MIME_TYPE, type, strlen(type), 0)) {
147 fprintf(stderr,
148 _("Could not save MIME type into extended attribute: %s\n"),
149 strerror(errno));
150 return -1;
152 #endif /* ENABLE_XATTR */
153 return 0;
156 #undef XATTR_MIME_TYPE
159 /* Open file and return descriptor. Return -1 in case of error. */
160 int open_file_for_writing(const char *file, _Bool truncate, _Bool overwrite) {
161 int fd;
163 if (!file) return -1;
165 fd = open(file, O_WRONLY|O_CREAT|O_APPEND |
166 ((truncate) ? O_TRUNC : 0) |
167 ((overwrite) ? 0 : O_EXCL),
168 0666);
169 if (fd == -1) {
170 fprintf(stderr, _("%s: Could not open file for writing: %s\n"),
171 file, strerror(errno));
174 return fd;
178 /* Create new file.
179 * @file is static template for file name. See mkstemps(3). It will contain
180 * new file name on return.
181 * @suffix_length is number of bytes of immutable file name suffix.
182 * @return descriptor of opened file, or -1 in case of error. */
183 int create_new_file(char *file, int suffix_length) {
184 int fd;
186 if (NULL == file) return -1;
188 /* TODO: mkstemps() works in CWD. Implement something working in $TMP. */
189 fd = mkstemps(file, suffix_length);
190 if (fd == -1) {
191 fprintf(stderr, _("Could not create temporary file `%s': %s\n"),
192 file, strerror(errno));
193 return -1;
196 return fd;
200 /* Return 0, -1 in case of error */
201 int mmap_file(const char *file, int *fd, void **buffer, size_t *length) {
202 struct stat file_info;
204 if (!file || !fd || !buffer || !length) return -1;
207 *fd = open(file, O_RDONLY);
208 if (*fd == -1) {
209 fprintf(stderr, _("%s: Could not open file: %s\n"),
210 file, strerror(errno));
211 return -1;
214 if (-1 == fstat(*fd, &file_info)) {
215 fprintf(stderr, _("%s: Could not get file size: %s\n"), file,
216 strerror(errno));
217 close(*fd);
218 return -1;
220 if (file_info.st_size < 0) {
221 fprintf(stderr, _("File `%s' has negative size: %jd\n"), file,
222 (intmax_t) file_info.st_size);
223 close(*fd);
224 return -1;
226 *length = file_info.st_size;
228 if (!*length) {
229 /* Empty region cannot be mmapped */
230 *buffer = NULL;
231 } else {
232 *buffer = mmap(NULL, *length, PROT_READ, MAP_PRIVATE, *fd, 0);
233 if (*buffer == MAP_FAILED) {
234 fprintf(stderr, _("%s: Could not map file to memory: %s\n"), file,
235 strerror(errno));
236 close(*fd);
237 return -1;
241 return 0;
245 /* Return 0, -1 in case of error */
246 int munmap_file(int fd, void *buffer, size_t length) {
247 int err = 0;
248 long int page_size = sysconf(_SC_PAGE_SIZE);
249 size_t pages = (length % page_size) ?
250 ((length / page_size) + 1) * page_size:
251 length;
253 if (length) {
254 err = munmap(buffer, pages);
255 if (err) {
256 fprintf(stderr,
257 _("Could not unmap memory at %p and length %zu: %s\n"),
258 buffer, pages, strerror(errno));
262 err = close(fd);
263 if (err) {
264 fprintf(stderr, _("Could close file descriptor %d: %s\n"), fd,
265 strerror(errno));
268 return err;
272 /* Return 0, -1 in case of error.
273 * @length and @mime_type are optional. */
274 int load_data_from_file(const char *file, void **data, size_t *length,
275 char **mime_type) {
276 int fd;
277 void *buffer;
278 size_t map_length;
280 if (!file || !data) return -1;
282 if (mmap_file(file, &fd, &buffer, &map_length)) return -1;
284 printf(ngettext("Reading %zu byte from file `%s'...\n",
285 "Reading %zu bytes from file `%s'...\n", map_length),
286 map_length, file);
287 *data = malloc(map_length);
288 if (!*data) {
289 fprintf(stderr, _("Error: Not enough memory\n"));
290 munmap_file(fd, buffer, map_length);
291 return -1;
293 memcpy(*data, buffer, map_length);
294 if (length) *length = map_length;
296 if (mime_type) {
297 if (get_mime_type(fd, file, mime_type))
298 fprintf(stderr, _("Warning: %s: Could not determine MIME type\n"),
299 file);
302 munmap_file(fd, buffer, map_length);
303 printf(_("Done.\n"));
304 return 0;
308 /* Save @data to file specified by descriptor @fd. If @fd is negative, @file
309 * file will be opened first. Descriptor is closed at the end of this
310 * function. Supply @file name even if @fd is positive, the name could be used
311 * in messages. */
312 int save_data_to_file(const char *file, int fd, const void *data,
313 const size_t length, const char *mime_type, _Bool overwrite) {
314 ssize_t written, left = length;
316 if (fd < 0 && !file) return -1;
317 if (length > 0 && !data) return -1;
319 if (fd < 0) {
320 fd = open_file_for_writing(file, 1, overwrite);
321 if (fd == -1) return -1;
324 if (file)
325 printf(ngettext("Writing %zu byte to file `%s'...\n",
326 "Writing %zu bytes to file `%s'...\n", length),
327 length, file);
328 else
329 printf(ngettext("Writing %zu byte to file desciptor %d...\n",
330 "Writing %zu bytes to file descriptor %d...\n", length),
331 length, fd);
332 while (left) {
333 written = write(fd, data + length - left, left);
334 if (written == -1) {
335 if (file)
336 fprintf(stderr, _("%s: Could not save file: %s\n"),
337 file, strerror(errno));
338 else
339 fprintf(stderr, _("Descriptor %d: Could not write into: %s\n"),
340 fd, strerror(errno));
341 close(fd);
342 return -1;
344 left-=written;
347 save_mime_type(fd, mime_type);
349 if (-1 == close(fd)) {
350 if (file)
351 fprintf(stderr, _("%s: Closing file failed: %s\n"),
352 file, strerror(errno));
353 else
354 fprintf(stderr, _("Descript %d: Closing failed: %s\n"),
355 fd, strerror(errno));
356 return -1;
359 printf(_("Done.\n"));
360 return 0;
364 /* @node_list is pointer to by-function allocated weak copy of libxml node
365 * pointers list. *NULL means empty list. It will be freed and NULLed in case
366 * of error.
367 * @xpat_expr is UTF-8 encoded XPath expression. */
368 static int xpath2nodelist(xmlNodePtr *node_list, xmlXPathContextPtr xpath_ctx,
369 const xmlChar *xpath_expr) {
370 xmlXPathObjectPtr result = NULL;
371 xmlNodePtr node = NULL, prev_node = NULL;
373 if (!node_list || !xpath_ctx || !xpath_expr) return -1;
375 result = xmlXPathEvalExpression(xpath_expr, xpath_ctx);
376 if (!result) {
377 char *xpath_expr_locale = utf82locale((const char*) xpath_expr);
378 fprintf(stderr, _("Error while evaluating XPath expression `%s'\n"),
379 xpath_expr_locale);
380 free(xpath_expr_locale);
381 return -1;
384 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
385 /* Empty match, returning empty node list */
386 *node_list = NULL;
387 } else {
388 /* Convert node set to list of siblings */
389 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
390 /* Make weak copy of the node */
391 node = malloc(sizeof(*node));
392 if (!node) {
393 fprintf(stderr, _("Not enough memory\n"));
394 xmlXPathFreeObject(result);
395 for (node = *node_list; node; node = node->next)
396 free(node);
397 *node_list = NULL;
398 return -1;
400 memcpy(node, result->nodesetval->nodeTab[i], sizeof(*node));
402 /* Add node to node_list */
403 node->prev = prev_node;
404 node->next = NULL;
405 if (prev_node)
406 prev_node->next = node;
407 else
408 *node_list = node;
409 prev_node = node;
411 /* Debug */
412 /*printf("* Embeding node #%d:\n", i);
413 xmlDebugDumpNode(stdout, node, 2);*/
417 xmlXPathFreeObject(result);
418 return 0;
422 /* Parse @buffer as XML document and return @node_list specified by
423 * @xpath_expression.
424 * @node_list is weak copy that must be non-recursively freed by caller. Caller
425 * must free XML document (accessible through @node_list member) on its own.
426 * In case of error @node_list value will be invalid.
427 * If @node_list is returned empty, function freed parsed document already.
428 * @xpat_expr is UTF-8 encoded XPath expression */
429 int load_xml_subtree_from_memory(const void *buffer, size_t length,
430 xmlNodePtr *node_list, const char *xpath_expr) {
431 int retval = 0;
432 xmlDocPtr xml = NULL;
433 xmlXPathContextPtr xpath_ctx = NULL;
435 if (!buffer || !length) {
436 fprintf(stderr,
437 _("Empty XML document, XPath expression cannot be evaluated\n"));
438 return -1;
440 if (!node_list || !xpath_expr)
441 return -1;
442 *node_list = NULL;
444 /* Create XML documents */
445 xml = xmlParseMemory(buffer, length);
446 if (!xml) {
447 fprintf(stderr, _("Error while parsing document\n"));
448 return -1;
451 xpath_ctx = xmlXPathNewContext(xml);
452 if (!xpath_ctx) {
453 fprintf(stderr, _("Error while creating XPath context\n"));
454 retval = -1;
455 goto leave;
458 if (xpath2nodelist(node_list, xpath_ctx, (const xmlChar *)xpath_expr)) {
459 char *xpath_expr_locale = utf82locale(xpath_expr);
460 fprintf(stderr, _("Could not convert XPath result to node list: %s\n"),
461 xpath_expr_locale);
462 free(xpath_expr_locale);
463 retval = -1;
464 goto leave;
467 leave:
468 xmlXPathFreeContext(xpath_ctx);
469 if (retval || !*node_list) xmlFreeDoc(xml);
470 return retval;
474 /* Deallocate struct isds_document with embedded XML recursively and NULL it */
475 void free_document_with_xml_node_list(struct isds_document **document) {
476 /* Free document xml_node_lists and associated XML document because
477 * they are weak copies and isds_document_free() does not free it. */
478 if (!document || !*document) return;
480 if ((*document)->is_xml && (*document)->xml_node_list) {
481 xmlFreeDoc((*document)->xml_node_list->doc);
482 for (xmlNodePtr node = (*document)->xml_node_list; node;
483 node = node->next)
484 free(node);
487 isds_document_free(document);
491 /* Serialize XML @node_list to automatically rellaocated libxml @buffer */
492 int serialize_xml_to_buffer(xmlBufferPtr *buffer, const xmlNodePtr node_list) {
493 int retval = 0;
494 xmlSaveCtxtPtr save_ctx = NULL;
495 xmlNodePtr element, subtree_copy = NULL;
496 xmlDocPtr subtree_doc = NULL;
498 if (!buffer) {
499 retval = -1;
500 goto leave;
502 if (*buffer) xmlBufferFree(*buffer);
504 /* Prepare buffer to serialize into */
505 *buffer = xmlBufferCreate();
506 if (!*buffer) {
507 fprintf(stderr,
508 _("Could not create buffer to serialize XML tree into\n"));
509 retval = -1;
510 goto leave;
512 save_ctx = xmlSaveToBuffer(*buffer, "UTF-8", 0);
513 if (!save_ctx) {
514 fprintf(stderr, _("Could not create XML serializer\n"));
515 retval = -1;
516 goto leave;
520 /* Select node and serialize it */
521 if (!node_list->next && node_list->type == XML_TEXT_NODE) {
522 /* One text node becomes plain text file */
523 /* TODO: Is CDATA expanded as text? Are entities expanded? */
524 /* XXX: According LibXML documentation, this function does not return
525 * meaningful value yet */
526 xmlSaveTree(save_ctx, node_list);
527 } else {
528 /* Serialize element */
529 if (!node_list->next && node_list->type == XML_ELEMENT_NODE) {
530 /* One element becomes root */
531 element = node_list;
532 } else {
533 /* Parent becomes root */
534 if (!node_list->parent) {
535 fprintf(stderr,
536 _("XML node list to serialize is missing parent node\n"));
537 retval = -1;
538 goto leave;
540 element = node_list->parent;
543 /* Use temporary XML document to resolve name space definitions */
544 /* XXX: We can not use xmlNodeDump() nor xmlSaveTree because it dumps
545 * the subtree as is. It can result in not well-formed on invalid XML
546 * tree (e.g. name space prefix definition can miss.) */
547 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
548 if (!subtree_doc) {
549 fprintf(stderr, _("Could not build temporary XML document\n"));
550 retval = -1;
551 goto leave;
553 /* XXX: Copy subtree and attach the copy to document.
554 * One node can not bee attached into more document at the same time. */
555 subtree_copy = xmlCopyNodeList(element);
556 if (!subtree_copy) {
557 fprintf(stderr, _("Could not copy XML subtree\n"));
558 retval = -1;
559 goto leave;
561 xmlDocSetRootElement(subtree_doc, subtree_copy);
562 /* Only this way we get name space definition as @xmlns:isds,
563 * otherwise we get name space prefix without definition */
564 /* FIXME: Don't overwrite original default name space */
565 /*isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
566 if(!isds_ns) {
567 isds_log_message(context, _("Could not create ISDS name space"));
568 err = IE_ERROR;
569 goto leave;
571 xmlSetNs(subtree_copy, isds_ns);*/
573 /* XXX: According LibXML documentation, this function does not return
574 * meaningful value yet */
575 xmlSaveDoc(save_ctx, subtree_doc);
578 /* Flush XML to buffer. After this call we are sure all data have been
579 * processed successfully. */
580 if (-1 == xmlSaveFlush(save_ctx)) {
581 fprintf(stderr, _("Could not serialize XML tree\n"));
582 retval = -1;
583 goto leave;
586 leave:
587 xmlFreeDoc(subtree_doc); /* Frees subtree_copy */
588 xmlSaveClose(save_ctx);
590 if (retval) {
591 xmlBufferFree(*buffer);
592 *buffer = NULL;
595 return retval;
599 /* Save @node_list to file specified by descriptor @fd. If @fd is negative, @file
600 * file will be opened first. Descriptor is closed at the end of this
601 * function. Supply @file name even if @fd is positive, the name could be used
602 * in messages. */
603 int save_xml_to_file(const char *file, int fd, const xmlNodePtr node_list,
604 const char *mime_type, _Bool overwrite) {
605 int retval = 0;
606 xmlBufferPtr buffer = NULL;
608 retval = serialize_xml_to_buffer(&buffer, node_list);
610 if (!retval) {
611 /* And save buffer into file */
612 retval = save_data_to_file(file, fd, buffer->content, buffer->use,
613 mime_type, overwrite);
614 xmlBufferFree(buffer);
617 return retval;
621 /* Return 0 if @path is directory, 1 if not, -1 if error occurred */
622 int is_directory(const char *path) {
623 struct stat dir_stat;
625 if (!path) return -1;
626 if (stat(path, &dir_stat)) return -1;
627 if S_ISDIR(dir_stat.st_mode) return 0;
628 else return 1;
631 /* Remove a file.
632 * @file to remove
633 * @return 0 on success, non-zero otherwise. */
634 int unlink_file(const char *file) {
635 if (NULL == file || !*file) return -1;
637 if (-1 == remove(file)) {
638 fprintf(stderr, _("Could not remove file `%s': %s\n"),
639 file, strerror(errno));
640 return -1;
643 return 0;