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
) {
161 if (!file
) return -1;
163 fd
= open(file
, O_WRONLY
|O_CREAT
|O_APPEND
| ((truncate
) ? O_TRUNC
: 0),
166 fprintf(stderr
, _("%s: Could not open file for writing: %s\n"),
167 file
, strerror(errno
));
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
);
183 fprintf(stderr
, _("%s: Could not open file: %s\n"),
184 file
, strerror(errno
));
188 if (-1 == fstat(*fd
, &file_info
)) {
189 fprintf(stderr
, _("%s: Could not get file size: %s\n"), file
,
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
);
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
,
214 /* Return 0, -1 in case of error */
215 int munmap_file(int fd
, void *buffer
, size_t length
) {
217 long int page_size
= sysconf(_SC_PAGE_SIZE
);
218 size_t pages
= (length
% page_size
) ?
219 ((length
/ page_size
) + 1) * page_size
:
222 err
= munmap(buffer
, pages
);
224 fprintf(stderr
, _("Could not unmap memory at %p and length %zu: %s\n"),
225 buffer
, pages
, strerror(errno
));
230 fprintf(stderr
, _("Could close file descriptor %d: %s\n"), fd
,
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
,
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
),
253 *data
= malloc(map_length
);
255 fprintf(stderr
, _("Error: Not enough memory\n"));
256 munmap_file(fd
, buffer
, map_length
);
259 memcpy(*data
, buffer
, map_length
);
260 if (length
) *length
= map_length
;
263 if (get_mime_type(fd
, file
, mime_type
))
264 fprintf(stderr
, _("Warning: %s: Could not determine MIME type\n"),
271 munmap_file(fd
, buffer
, map_length
);
272 printf(_("Done.\n"));
277 int save_data_to_file(const char *file
, const void *data
,
278 const size_t length
, const char *mime_type
) {
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
);
291 written
= write(fd
, data
+ length
- left
, left
);
293 fprintf(stderr
, _("%s: Could not save file: %s\n"),
294 file
, strerror(errno
));
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
));
309 printf(_("Done.\n"));
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
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
);
327 char *xpath_expr_locale
= utf82locale((const char*) xpath_expr
);
328 fprintf(stderr
, _("Error while evaluating XPath expression `%s'\n"),
330 free(xpath_expr_locale
);
334 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
335 /* Empty match, returning empty node list */
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
));
343 fprintf(stderr
, _("Not enough memory\n"));
344 xmlXPathFreeObject(result
);
345 for (node
= *node_list
; node
; node
= node
->next
)
350 memcpy(node
, result
->nodesetval
->nodeTab
[i
], sizeof(*node
));
352 /* Add node to node_list */
353 node
->prev
= prev_node
;
356 prev_node
->next
= node
;
362 /*printf("* Embeding node #%d:\n", i);
363 xmlDebugDumpNode(stdout, node, 2);*/
367 xmlXPathFreeObject(result
);
372 /* Parse @buffer as XML document and return @node_list specified by
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
) {
382 xmlDocPtr xml
= NULL
;
383 xmlXPathContextPtr xpath_ctx
= NULL
;
385 if (!buffer
|| !length
) {
387 _("Empty XML document, XPath expression cannot be evaluated\n"));
390 if (!node_list
|| !xpath_expr
)
394 /* Create XML documents */
395 xml
= xmlParseMemory(buffer
, length
);
397 fprintf(stderr
, _("Error while parsing document\n"));
401 xpath_ctx
= xmlXPathNewContext(xml
);
403 fprintf(stderr
, _("Error while creating XPath context\n"));
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"),
412 free(xpath_expr_locale
);
418 xmlXPathFreeContext(xpath_ctx
);
419 if (retval
|| !*node_list
) xmlFreeDoc(xml
);
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
;
437 isds_document_free(document
);
441 int save_xml_to_file(const char *file
, const xmlNodePtr node_list
,
442 const char *mime_type
) {
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();
454 _("Could not create buffer to serialize XML tree into"));
458 save_ctx
= xmlSaveToBuffer(buffer
, "UTF-8", 0);
460 fprintf(stderr
, _("Could not create XML serializer"));
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
);
474 /* Serialize element */
475 if (!node_list
->next
&& node_list
->type
== XML_ELEMENT_NODE
) {
476 /* One element becomes root */
479 /* Parent becomes root */
480 if (!node_list
->parent
) {
482 _("XML node list to serialize is missing parent node"));
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");
495 fprintf(stderr
, _("Could not build temporary XML document"));
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
);
503 fprintf(stderr
, _("Could not copy XML subtree"));
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);
513 isds_log_message(context, _("Could not create ISDS name space"));
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"));
533 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy */
534 xmlSaveClose(save_ctx
);
537 /* And save buffer into file */
538 retval
= save_data_to_file(file
, buffer
->content
, buffer
->use
,
542 xmlBufferFree(buffer
);
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;