1 #define _XOPEN_SOURCE 500
12 #include <limits.h> /* SSIZE_MAX */
14 #include <libxml/xmlsave.h> /* For XML serializator */
15 #include <libxml/xpath.h> /* For XPath */
17 #include "shigofumi.h"
20 #include <attr/xattr.h>
24 /* Guess MIME type of @file by content.
25 * @type is automatically allocated. Return 0, -1 and * NULL @type in case
27 static int get_mime_type_by_magic(const char *file
, char **type
) {
29 const char *magic_guess
;
33 if (!file
|| !*file
) return -1;
35 if (!(magic
= magic_open(MAGIC_MIME_TYPE
))) {
36 fprintf(stderr
, _("Could not initialize libmagic\n"));
39 if (magic_load(magic
, NULL
)) {
40 fprintf(stderr
, _("Could not load magic database\n"));
44 if (!(magic_guess
= magic_file(magic
, file
))) {
45 fprintf(stderr
, _("%s: Could not guess MIME type: %s\n"),
46 file
, magic_error(magic
));
51 if (!(*type
= strdup(magic_guess
))) {
52 fprintf(stderr
, _("Not enough memory\n"));
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
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
) {
73 ssize_t length
= 0, ret
;
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);
87 fprintf(stderr
, _("Not enough memory\n"));
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"));
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;
123 if ((!name
|| !*name
) && fd
== -1) return -1;
126 if (get_mime_type_by_xattr(fd
, type
))
128 if (get_mime_type_by_magic(name
, type
))
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;
144 if (fsetxattr(fd
, XATTR_MIME_TYPE
, type
, strlen(type
), 0)) {
146 _("Could not save MIME type into extended attribute: %s\n"),
150 #endif /* ENABLE_XATTR */
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
, _Bool overwrite
) {
161 if (!file
) return -1;
163 fd
= open(file
, O_WRONLY
|O_CREAT
|O_APPEND
|
164 ((truncate
) ? O_TRUNC
: 0) |
165 ((overwrite
) ? 0 : O_EXCL
),
168 fprintf(stderr
, _("%s: Could not open file for writing: %s\n"),
169 file
, strerror(errno
));
176 /* Return 0, -1 in case of error */
177 int mmap_file(const char *file
, int *fd
, void **buffer
, size_t *length
) {
178 struct stat file_info
;
180 if (!file
|| !fd
|| !buffer
|| !length
) return -1;
183 *fd
= open(file
, O_RDONLY
);
185 fprintf(stderr
, _("%s: Could not open file: %s\n"),
186 file
, strerror(errno
));
190 if (-1 == fstat(*fd
, &file_info
)) {
191 fprintf(stderr
, _("%s: Could not get file size: %s\n"), file
,
196 if (file_info
.st_size
< 0) {
197 fprintf(stderr
, _("File `%s' has negative size: %jd\n"), file
,
198 (intmax_t) file_info
.st_size
);
202 *length
= file_info
.st_size
;
205 /* Empty region cannot be mmapped */
208 *buffer
= mmap(NULL
, *length
, PROT_READ
, MAP_PRIVATE
, *fd
, 0);
209 if (*buffer
== MAP_FAILED
) {
210 fprintf(stderr
, _("%s: Could not map file to memory: %s\n"), file
,
221 /* Return 0, -1 in case of error */
222 int munmap_file(int fd
, void *buffer
, size_t length
) {
224 long int page_size
= sysconf(_SC_PAGE_SIZE
);
225 size_t pages
= (length
% page_size
) ?
226 ((length
/ page_size
) + 1) * page_size
:
230 err
= munmap(buffer
, pages
);
233 _("Could not unmap memory at %p and length %zu: %s\n"),
234 buffer
, pages
, strerror(errno
));
240 fprintf(stderr
, _("Could close file descriptor %d: %s\n"), fd
,
248 /* Return 0, -1 in case of error.
249 * @length and @mime_type are optional. */
250 int load_data_from_file(const char *file
, void **data
, size_t *length
,
256 if (!file
|| !data
) return -1;
258 if (mmap_file(file
, &fd
, &buffer
, &map_length
)) return -1;
260 printf(ngettext("Reading %zu byte from file `%s'...\n",
261 "Reading %zu bytes from file `%s'...\n", map_length
),
263 *data
= malloc(map_length
);
265 fprintf(stderr
, _("Error: Not enough memory\n"));
266 munmap_file(fd
, buffer
, map_length
);
269 memcpy(*data
, buffer
, map_length
);
270 if (length
) *length
= map_length
;
273 if (get_mime_type(fd
, file
, mime_type
))
274 fprintf(stderr
, _("Warning: %s: Could not determine MIME type\n"),
281 munmap_file(fd
, buffer
, map_length
);
282 printf(_("Done.\n"));
287 /* Save @data to file specified by descriptor @fd. If @fd is negative, @file
288 * file will be opened first. Descriptor is closed at the end of this
289 * function. Supply @file name even if @fd is positive, the name could be used
291 int save_data_to_file(const char *file
, int fd
, const void *data
,
292 const size_t length
, const char *mime_type
, _Bool overwrite
) {
293 ssize_t written
, left
= length
;
295 if (fd
< 0 && !file
) return -1;
296 if (length
> 0 && !data
) return -1;
299 fd
= open_file_for_writing(file
, 1, overwrite
);
300 if (fd
== -1) return -1;
304 printf(ngettext("Writing %zu byte to file `%s'...\n",
305 "Writing %zu bytes to file `%s'...\n", length
),
308 printf(ngettext("Writing %zu byte to file desciptor %d...\n",
309 "Writing %zu bytes to file descriptor %d...\n", length
),
312 written
= write(fd
, data
+ length
- left
, left
);
315 fprintf(stderr
, _("%s: Could not save file: %s\n"),
316 file
, strerror(errno
));
318 fprintf(stderr
, _("Descriptor %d: Could not write into: %s\n"),
319 fd
, strerror(errno
));
326 save_mime_type(fd
, mime_type
);
328 if (-1 == close(fd
)) {
330 fprintf(stderr
, _("%s: Closing file failed: %s\n"),
331 file
, strerror(errno
));
333 fprintf(stderr
, _("Descript %d: Closing failed: %s\n"),
334 fd
, strerror(errno
));
338 printf(_("Done.\n"));
343 /* @node_list is pointer to by-function allocated weak copy of libxml node
344 * pointers list. *NULL means empty list. It will be freed and NULLed in case
346 * @xpat_expr is UTF-8 encoded XPath expression. */
347 static int xpath2nodelist(xmlNodePtr
*node_list
, xmlXPathContextPtr xpath_ctx
,
348 const xmlChar
*xpath_expr
) {
349 xmlXPathObjectPtr result
= NULL
;
350 xmlNodePtr node
= NULL
, prev_node
= NULL
;
352 if (!node_list
|| !xpath_ctx
|| !xpath_expr
) return -1;
354 result
= xmlXPathEvalExpression(xpath_expr
, xpath_ctx
);
356 char *xpath_expr_locale
= utf82locale((const char*) xpath_expr
);
357 fprintf(stderr
, _("Error while evaluating XPath expression `%s'\n"),
359 free(xpath_expr_locale
);
363 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
364 /* Empty match, returning empty node list */
367 /* Convert node set to list of siblings */
368 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
369 /* Make weak copy of the node */
370 node
= malloc(sizeof(*node
));
372 fprintf(stderr
, _("Not enough memory\n"));
373 xmlXPathFreeObject(result
);
374 for (node
= *node_list
; node
; node
= node
->next
)
379 memcpy(node
, result
->nodesetval
->nodeTab
[i
], sizeof(*node
));
381 /* Add node to node_list */
382 node
->prev
= prev_node
;
385 prev_node
->next
= node
;
391 /*printf("* Embeding node #%d:\n", i);
392 xmlDebugDumpNode(stdout, node, 2);*/
396 xmlXPathFreeObject(result
);
401 /* Parse @buffer as XML document and return @node_list specified by
403 * @node_list is weak copy that must be non-recursively freed by caller. Caller
404 * must free XML document (accessible through @node_list member) on its own.
405 * In case of error @node_list value will be invalid.
406 * If @node_list is returned empty, function freed parsed document already.
407 * @xpat_expr is UTF-8 encoded XPath expression */
408 int load_xml_subtree_from_memory(const void *buffer
, size_t length
,
409 xmlNodePtr
*node_list
, const char *xpath_expr
) {
411 xmlDocPtr xml
= NULL
;
412 xmlXPathContextPtr xpath_ctx
= NULL
;
414 if (!buffer
|| !length
) {
416 _("Empty XML document, XPath expression cannot be evaluated\n"));
419 if (!node_list
|| !xpath_expr
)
423 /* Create XML documents */
424 xml
= xmlParseMemory(buffer
, length
);
426 fprintf(stderr
, _("Error while parsing document\n"));
430 xpath_ctx
= xmlXPathNewContext(xml
);
432 fprintf(stderr
, _("Error while creating XPath context\n"));
437 if (xpath2nodelist(node_list
, xpath_ctx
, (const xmlChar
*)xpath_expr
)) {
438 char *xpath_expr_locale
= utf82locale(xpath_expr
);
439 fprintf(stderr
, _("Could not convert XPath result to node list: %s\n"),
441 free(xpath_expr_locale
);
447 xmlXPathFreeContext(xpath_ctx
);
448 if (retval
|| !*node_list
) xmlFreeDoc(xml
);
453 /* Deallocate struct isds_document with embedded XML recursively and NULL it */
454 void free_document_with_xml_node_list(struct isds_document
**document
) {
455 /* Free document xml_node_lists and associated XML document because
456 * they are weak copies and isds_document_free() does not free it. */
457 if (!document
|| !*document
) return;
459 if ((*document
)->is_xml
&& (*document
)->xml_node_list
) {
460 xmlFreeDoc((*document
)->xml_node_list
->doc
);
461 for (xmlNodePtr node
= (*document
)->xml_node_list
; node
;
466 isds_document_free(document
);
470 /* Serialize XML @node_list to automatically rellaocated libxml @buffer */
471 int serialize_xml_to_buffer(xmlBufferPtr
*buffer
, const xmlNodePtr node_list
) {
473 xmlSaveCtxtPtr save_ctx
= NULL
;
474 xmlNodePtr element
, subtree_copy
= NULL
;
475 xmlDocPtr subtree_doc
= NULL
;
481 if (*buffer
) xmlBufferFree(*buffer
);
483 /* Prepare buffer to serialize into */
484 *buffer
= xmlBufferCreate();
487 _("Could not create buffer to serialize XML tree into\n"));
491 save_ctx
= xmlSaveToBuffer(*buffer
, "UTF-8", 0);
493 fprintf(stderr
, _("Could not create XML serializer\n"));
499 /* Select node and serialize it */
500 if (!node_list
->next
&& node_list
->type
== XML_TEXT_NODE
) {
501 /* One text node becomes plain text file */
502 /* TODO: Is CDATA expanded as text? Are entities expanded? */
503 /* XXX: According LibXML documentation, this function does not return
504 * meaningful value yet */
505 xmlSaveTree(save_ctx
, node_list
);
507 /* Serialize element */
508 if (!node_list
->next
&& node_list
->type
== XML_ELEMENT_NODE
) {
509 /* One element becomes root */
512 /* Parent becomes root */
513 if (!node_list
->parent
) {
515 _("XML node list to serialize is missing parent node\n"));
519 element
= node_list
->parent
;
522 /* Use temporary XML document to resolve name space definitions */
523 /* XXX: We can not use xmlNodeDump() nor xmlSaveTree because it dumps
524 * the subtree as is. It can result in not well-formed on invalid XML
525 * tree (e.g. name space prefix definition can miss.) */
526 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
528 fprintf(stderr
, _("Could not build temporary XML document\n"));
532 /* XXX: Copy subtree and attach the copy to document.
533 * One node can not bee attached into more document at the same time. */
534 subtree_copy
= xmlCopyNodeList(element
);
536 fprintf(stderr
, _("Could not copy XML subtree\n"));
540 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
541 /* Only this way we get name space definition as @xmlns:isds,
542 * otherwise we get name space prefix without definition */
543 /* FIXME: Don't overwrite original default name space */
544 /*isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
546 isds_log_message(context, _("Could not create ISDS name space"));
550 xmlSetNs(subtree_copy, isds_ns);*/
552 /* XXX: According LibXML documentation, this function does not return
553 * meaningful value yet */
554 xmlSaveDoc(save_ctx
, subtree_doc
);
557 /* Flush XML to buffer. After this call we are sure all data have been
558 * processed successfully. */
559 if (-1 == xmlSaveFlush(save_ctx
)) {
560 fprintf(stderr
, _("Could not serialize XML tree\n"));
566 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy */
567 xmlSaveClose(save_ctx
);
570 xmlBufferFree(*buffer
);
578 /* Save @node_list to file specified by descriptor @fd. If @fd is negative, @file
579 * file will be opened first. Descriptor is closed at the end of this
580 * function. Supply @file name even if @fd is positive, the name could be used
582 int save_xml_to_file(const char *file
, int fd
, const xmlNodePtr node_list
,
583 const char *mime_type
, _Bool overwrite
) {
585 xmlBufferPtr buffer
= NULL
;
587 retval
= serialize_xml_to_buffer(&buffer
, node_list
);
590 /* And save buffer into file */
591 retval
= save_data_to_file(file
, fd
, buffer
->content
, buffer
->use
,
592 mime_type
, overwrite
);
593 xmlBufferFree(buffer
);
600 /* Return 0 if @path is directory, 1 if not, -1 if error occurred */
601 int is_directory(const char *path
) {
602 struct stat dir_stat
;
604 if (!path
) return -1;
605 if (stat(path
, &dir_stat
)) return -1;
606 if S_ISDIR(dir_stat
.st_mode
) return 0;