diff options
Diffstat (limited to 'src/xine-utils')
-rw-r--r-- | src/xine-utils/Makefile.am | 43 | ||||
-rw-r--r-- | src/xine-utils/attributes.h | 24 | ||||
-rw-r--r-- | src/xine-utils/cpu_accel.c | 34 | ||||
-rw-r--r-- | src/xine-utils/memcpy.c | 6 | ||||
-rw-r--r-- | src/xine-utils/monitor.c | 11 | ||||
-rw-r--r-- | src/xine-utils/utils.c | 57 | ||||
-rw-r--r-- | src/xine-utils/xineutils.h | 10 | ||||
-rw-r--r-- | src/xine-utils/xmllexer.c | 140 | ||||
-rw-r--r-- | src/xine-utils/xmllexer.h | 2 | ||||
-rw-r--r-- | src/xine-utils/xmlparser.c | 308 | ||||
-rw-r--r-- | src/xine-utils/xmlparser.h | 15 |
11 files changed, 486 insertions, 164 deletions
diff --git a/src/xine-utils/Makefile.am b/src/xine-utils/Makefile.am index 95de06b9e..10f1714f9 100644 --- a/src/xine-utils/Makefile.am +++ b/src/xine-utils/Makefile.am @@ -1,19 +1,33 @@ include $(top_srcdir)/misc/Makefile.common -LIBTOOL = $(SHELL) $(top_builddir)/libtool -noinst_LTLIBRARIES = libxineutils.la +AM_CFLAGS = $(DEFAULT_OCFLAGS) $(X_CFLAGS) $(VISIBILITY_FLAG) +AM_CPPFLAGS = -DXINE_LIBRARY_COMPILE EXTRA_DIST = ppcasm_string.S ppc_asm.tmpl -if PPC_ARCH +noinst_HEADERS = ppcasm_string.h xine_check.h + +xineinclude_HEADERS = \ + attributes.h \ + compat.h \ + xine_buffer.h \ + xineutils.h \ + xmllexer.h \ + xmlparser.h \ + list.h \ + array.h \ + sorted_array.h \ + pool.h \ + ring_buffer.h + +noinst_LTLIBRARIES = libxineutils.la + +if ARCH_PPC if !HOST_OS_DARWIN pppc_files = ppcasm_string.S endif endif -AM_CFLAGS = $(X_CFLAGS) $(VISIBILITY_FLAG) -AM_CPPFLAGS=-DXINE_LIBRARY_COMPILE - libxineutils_la_SOURCES = $(pppc_files) \ cpu_accel.c \ color.c \ @@ -31,20 +45,3 @@ libxineutils_la_SOURCES = $(pppc_files) \ sorted_array.c \ pool.c \ ring_buffer.c - -xineinclude_HEADERS = \ - attributes.h \ - compat.h \ - xine_buffer.h \ - xineutils.h \ - xmllexer.h \ - xmlparser.h \ - list.h \ - array.h \ - sorted_array.h \ - pool.h \ - ring_buffer.h - - -noinst_HEADERS = ppcasm_string.h xine_check.h - diff --git a/src/xine-utils/attributes.h b/src/xine-utils/attributes.h index 563832e5c..b25c76572 100644 --- a/src/xine-utils/attributes.h +++ b/src/xine-utils/attributes.h @@ -32,18 +32,6 @@ #define ATTR_ALIGN(align) #endif -/* disable GNU __attribute__ extension, when not compiling with GNU C */ -#if defined(__GNUC__) || defined (__ICC) -#ifndef ATTRIBUTE_PACKED -#define ATTRIBUTE_PACKED 1 -#endif -#else -#undef ATTRIBUTE_PACKED -#ifndef __attribute__ -#define __attribute__(x) /**/ -#endif /* __attribute __*/ -#endif - #ifdef XINE_COMPILE # include "configure.h" #endif @@ -83,4 +71,16 @@ # define XINE_FORMAT_PRINTF_ARG(fmt) #endif +#ifdef SUPPORT_ATTRIBUTE_PACKED +# define XINE_PACKED __attribute__((packed)) +#else +# define XINE_PACKED +#endif + +#ifdef SUPPORT_ATTRIBUTE_MALLOC +# define XINE_MALLOC __attribute__((__malloc__)) +#else +# define XINE_MALLOC +#endif + #endif /* ATTRIBUTE_H_ */ diff --git a/src/xine-utils/cpu_accel.c b/src/xine-utils/cpu_accel.c index c241dd7ef..8bad23db9 100644 --- a/src/xine-utils/cpu_accel.c +++ b/src/xine-utils/cpu_accel.c @@ -24,9 +24,10 @@ #include <stdio.h> #include <stdlib.h> #include <inttypes.h> -#include <signal.h> -#include <setjmp.h> + +#if defined(HAVE_MLIB) && defined(MLIB_LAZYLOAD) #include <dlfcn.h> +#endif #if defined (__SVR4) && defined (__sun) #include <sys/systeminfo.h> @@ -40,22 +41,32 @@ #include "xineutils.h" -#if defined(ARCH_X86) || defined(ARCH_X86_64) +#if defined(__i386__) || defined(__x86_64__) + +#ifndef __x86_64__ +#include <signal.h> +#include <setjmp.h> static jmp_buf sigill_return; static void sigill_handler (int n) { longjmp(sigill_return, 1); } +#endif static uint32_t arch_accel (void) { uint32_t caps; -#ifdef __x86_64__ +#if defined(__x86_64__) || \ + ( defined(__SSE__) && defined(__SSE2__) && defined(__MMX__) ) /* No need to test for this on AMD64, we know what the platform has. */ - caps = MM_ACCEL_X86_MMX | MM_ACCEL_X86_SSE | MM_ACCEL_X86_MMXEXT | MM_ACCEL_X86_SSE2; + caps = MM_ACCEL_X86_MMX | MM_ACCEL_X86_SSE | MM_ACCEL_X86_MMXEXT | MM_ACCEL_X86_SSE2 +# if defined(__3dNOW__) + | MM_ACCEL_X86_3DNOW +# endif + ; #else #ifndef _MSC_VER @@ -148,6 +159,9 @@ static uint32_t arch_accel (void) caps = 0; #endif /* _MSC_VER */ +#endif /* x86_64 or built-in options */ + +#ifndef __x86_64__ /* test OS support for SSE */ if (caps & MM_ACCEL_X86_SSE) { void (*old_sigill_handler)(int); @@ -169,9 +183,12 @@ static uint32_t arch_accel (void) return caps; } -#endif /* ARCH_X86 */ +#endif /* i386 or x86_64 */ #if defined(ARCH_PPC) && defined(ENABLE_ALTIVEC) +#include <signal.h> +#include <setjmp.h> + static sigjmp_buf jmpbuf; static volatile sig_atomic_t canjump = 0; @@ -256,6 +273,9 @@ static uint32_t arch_accel (void) return flags; } #else +#include <signal.h> +#include <setjmp.h> + static sigjmp_buf jmpbuf; static volatile sig_atomic_t canjump = 0; @@ -326,7 +346,7 @@ uint32_t xine_mm_accel (void) #endif #endif -#if defined(ARCH_X86) || defined(ARCH_X86_64) || (defined(ARCH_PPC) && defined(ENABLE_ALTIVEC)) || (defined(ARCH_SPARC) && defined(ENABLE_VIS)) +#if defined(__i386__) || defined(__x86_64__) || (defined(ARCH_PPC) && defined(ENABLE_ALTIVEC)) || (defined(ARCH_SPARC) && defined(ENABLE_VIS)) accel |= arch_accel(); #endif diff --git a/src/xine-utils/memcpy.c b/src/xine-utils/memcpy.c index 1b3e38fd1..da4b83b09 100644 --- a/src/xine-utils/memcpy.c +++ b/src/xine-utils/memcpy.c @@ -383,8 +383,8 @@ static void *linux_kernel_memcpy(void *to, const void *from, size_t len) { #endif /* ARCH_X86 */ static struct { - char *name; - void *(* function)(void *to, const void *from, size_t len); + char *const name; + void *(*const function)(void *to, const void *from, size_t len); uint64_t time; /* This type could be used for non-MSC build too! */ @@ -461,7 +461,7 @@ void xine_probe_fast_memcpy(xine_t *xine) char *buf1, *buf2; int i, j, best; int config_flags = -1; - static const char *memcpy_methods[] = { + static const char *const memcpy_methods[] = { "probe", "libc", #if (defined(ARCH_X86) || defined(ARCH_X86_64)) && !defined(_MSC_VER) "kernel", "mmx", "mmxext", "sse", diff --git a/src/xine-utils/monitor.c b/src/xine-utils/monitor.c index fb323055c..3c7c3e10a 100644 --- a/src/xine-utils/monitor.c +++ b/src/xine-utils/monitor.c @@ -38,13 +38,10 @@ static long profiler_calls[MAX_ID] ; static const char *profiler_label[MAX_ID] ; void xine_profiler_init () { - int i; - for (i=0; i<MAX_ID; i++) { - profiler_times[i] = 0; - profiler_start[i] = 0; - profiler_calls[i] = 0; - profiler_label[i] = NULL; - } + memset(profiler_times, 0, sizeof(profiler_times)); + memset(profiler_start, 0, sizeof(profiler_start)); + memset(profiler_calls, 0, sizeof(profiler_calls)); + memset(profiler_label, 0, sizeof(profiler_label)); } int xine_profiler_allocate_slot (const char *label) { diff --git a/src/xine-utils/utils.c b/src/xine-utils/utils.c index 47c32b713..980a2a520 100644 --- a/src/xine-utils/utils.c +++ b/src/xine-utils/utils.c @@ -253,6 +253,27 @@ void *xine_xmalloc(size_t size) { return ptr; } +/** + * @brief Wrapper around calloc() function. + * @param nmemb Number of elements to allocate + * @param size Size of each element to allocate + * + * This is a simple wrapper around calloc(), the only thing + * it does more than calloc() is outputting an error if + * the calloc fails (returning NULL). + */ +void *xine_xcalloc(size_t nmemb, size_t size) { + void *ptr; + + if((ptr = calloc(nmemb, size)) == NULL) { + fprintf(stderr, "%s: calloc() failed: %s.\n", + __XINE_FUNCTION__, strerror(errno)); + return NULL; + } + + return ptr; +} + void *xine_xmalloc_aligned(size_t alignment, size_t size, void **base) { char *ptr; @@ -450,38 +471,40 @@ void xine_usec_sleep(unsigned usec) { /* print a hexdump of length bytes from the data given in buf */ -void xine_hexdump (const char *buf, int length) { - int i,j; - unsigned char c; +void xine_hexdump (const void *buf_gen, int length) { + static const char separator[70] = "---------------------------------------------------------------------"; + + const uint8_t *const buf = (const uint8_t*)buf; + int j = 0; /* printf ("Hexdump: %i Bytes\n", length);*/ - for(j=0; j<69; j++) - printf ("-"); - printf ("\n"); + puts(separator); - j=0; while(j<length) { + int i; + const int imax = (j+16 < length) ? (j+16) : length; + printf ("%04X ",j); for (i=j; i<j+16; i++) { if( i<length ) - printf ("%02X ", (unsigned char) buf[i]); + printf ("%02X ", buf[i]); else printf(" "); } - for (i=j;i<(j+16<length?j+16:length);i++) { - c=buf[i]; + + for (i=j; i < imax; i++) { + uint8_t c = buf[i]; if ((c>=32) && (c<127)) - printf ("%c", c); - else - printf ("."); + c = '.'; + + fputc(c, stdout); } j=i; - printf("\n"); + + fputc('\n', stdout); } - for(j=0; j<69; j++) - printf("-"); - printf("\n"); + puts(separator); } diff --git a/src/xine-utils/xineutils.h b/src/xine-utils/xineutils.h index 120cb3578..c7cfe980e 100644 --- a/src/xine-utils/xineutils.h +++ b/src/xine-utils/xineutils.h @@ -621,11 +621,9 @@ void xine_profiler_print_results (void) XINE_PROTECTED; * Allocate and clean memory size_t 'size', then return the pointer * to the allocated memory. */ -#if !defined(__GNUC__) || __GNUC__ < 3 -void *xine_xmalloc(size_t size) XINE_PROTECTED; -#else -void *xine_xmalloc(size_t size) __attribute__ ((__malloc__)) XINE_PROTECTED; -#endif +void *xine_xmalloc(size_t size) XINE_MALLOC XINE_PROTECTED; + +void *xine_xcalloc(size_t nmemb, size_t size) XINE_MALLOC XINE_PROTECTED; /* * Same as above, but memory is aligned to 'alignement'. @@ -783,7 +781,7 @@ extern void yuy2_to_yuy2 int width, int height) XINE_PROTECTED; /* print a hexdump of the given data */ -void xine_hexdump (const char *buf, int length) XINE_PROTECTED; +void xine_hexdump (const void *buf, int length) XINE_PROTECTED; /* * Optimization macros for conditions diff --git a/src/xine-utils/xmllexer.c b/src/xine-utils/xmllexer.c index 8879f7d0c..39632938f 100644 --- a/src/xine-utils/xmllexer.c +++ b/src/xine-utils/xmllexer.c @@ -41,16 +41,19 @@ #endif /* private constants*/ -#define NORMAL 0 /* normal lex mode */ -#define DATA 1 /* data lex mode */ /* private global variables */ static const char * lexbuf; static int lexbuf_size = 0; static int lexbuf_pos = 0; -static int lex_mode = NORMAL; static int in_comment = 0; +static enum { + NORMAL, + DATA, + CDATA, +} lex_mode = NORMAL; + void lexer_init(const char * buf, int size) { lexbuf = buf; lexbuf_size = size; @@ -61,83 +64,102 @@ void lexer_init(const char * buf, int size) { lprintf("buffer length %d\n", size); } +typedef enum { + STATE_UNKNOWN = -1, + STATE_IDLE, + STATE_EOL, + STATE_SEPAR, + STATE_T_M_START, + STATE_T_M_STOP_1, + STATE_T_M_STOP_2, + STATE_T_EQUAL, + STATE_T_STRING_SINGLE, + STATE_T_STRING_DOUBLE, + STATE_T_COMMENT, + STATE_T_TI_STOP, + STATE_T_DASHDASH, + STATE_T_C_STOP, + STATE_IDENT /* must be last */ +} lexer_state_t; + int lexer_get_token(char * tok, int tok_size) { int tok_pos = 0; - int state = 0; + lexer_state_t state = STATE_IDLE; char c; if (tok) { while ((tok_pos < tok_size) && (lexbuf_pos < lexbuf_size)) { c = lexbuf[lexbuf_pos]; - lprintf("c=%c, state=%d, in_comment=%d\n", c, state, in_comment); + lprintf("c=%c, state=%d, lex_mode=%d, in_comment=%d\n", c, state, lex_mode, in_comment); - if (lex_mode == NORMAL) { - /* normal mode */ + switch (lex_mode) { + case NORMAL: switch (state) { /* init state */ - case 0: + case STATE_IDLE: switch (c) { case '\n': case '\r': - state = 1; + state = STATE_EOL; tok[tok_pos] = c; tok_pos++; break; case ' ': case '\t': - state = 2; + state = STATE_SEPAR; tok[tok_pos] = c; tok_pos++; break; case '<': - state = 3; + state = STATE_T_M_START; tok[tok_pos] = c; tok_pos++; break; case '>': - state = 4; + state = STATE_T_M_STOP_1; tok[tok_pos] = c; tok_pos++; break; case '/': if (!in_comment) - state = 5; + state = STATE_T_M_STOP_2; tok[tok_pos] = c; tok_pos++; break; case '=': - state = 6; + state = STATE_T_EQUAL; tok[tok_pos] = c; tok_pos++; break; case '\"': /* " */ - state = 7; + state = STATE_T_STRING_DOUBLE; break; case '\'': /* " */ - state = 12; + state = STATE_T_STRING_SINGLE; break; case '-': - state = 10; + state = STATE_T_DASHDASH; tok[tok_pos] = c; tok_pos++; break; case '?': - state = 9; + if (!in_comment) + state = STATE_T_TI_STOP; tok[tok_pos] = c; tok_pos++; break; default: - state = 100; + state = STATE_IDENT; tok[tok_pos] = c; tok_pos++; break; @@ -146,7 +168,7 @@ int lexer_get_token(char * tok, int tok_size) { break; /* end of line */ - case 1: + case STATE_EOL: if (c == '\n' || (c == '\r')) { tok[tok_pos] = c; lexbuf_pos++; @@ -158,7 +180,7 @@ int lexer_get_token(char * tok, int tok_size) { break; /* T_SEPAR */ - case 2: + case STATE_SEPAR: if (c == ' ' || (c == '\t')) { tok[tok_pos] = c; lexbuf_pos++; @@ -170,7 +192,7 @@ int lexer_get_token(char * tok, int tok_size) { break; /* T_M_START < or </ or <! or <? */ - case 3: + case STATE_T_M_START: switch (c) { case '/': tok[tok_pos] = c; @@ -183,7 +205,7 @@ int lexer_get_token(char * tok, int tok_size) { tok[tok_pos] = c; lexbuf_pos++; tok_pos++; - state = 8; + state = STATE_T_COMMENT; break; case '?': tok[tok_pos] = c; @@ -199,7 +221,7 @@ int lexer_get_token(char * tok, int tok_size) { break; /* T_M_STOP_1 */ - case 4: + case STATE_T_M_STOP_1: tok[tok_pos] = '\0'; if (!in_comment) lex_mode = DATA; @@ -207,7 +229,7 @@ int lexer_get_token(char * tok, int tok_size) { break; /* T_M_STOP_2 */ - case 5: + case STATE_T_M_STOP_2: if (c == '>') { tok[tok_pos] = c; lexbuf_pos++; @@ -223,13 +245,13 @@ int lexer_get_token(char * tok, int tok_size) { break; /* T_EQUAL */ - case 6: + case STATE_T_EQUAL: tok[tok_pos] = '\0'; return T_EQUAL; break; /* T_STRING */ - case 7: + case STATE_T_STRING_DOUBLE: tok[tok_pos] = c; lexbuf_pos++; if (c == '\"') { /* " */ @@ -239,8 +261,8 @@ int lexer_get_token(char * tok, int tok_size) { tok_pos++; break; - /* T_C_START or T_DOCTYPE_START */ - case 8: + /* T_C_START or T_DOCTYPE_START or T_CDATA_START */ + case STATE_T_COMMENT: switch (c) { case '-': lexbuf_pos++; @@ -264,6 +286,17 @@ int lexer_get_token(char * tok, int tok_size) { return T_ERROR; } break; + case '[': + lexbuf_pos++; + if (strncmp(lexbuf + lexbuf_pos, "CDATA[", 6) == 0) { + strncpy (tok + tok_pos, "[CDATA[", 7); /* FIXME */ + lexbuf_pos += 6; + lex_mode = CDATA; + return T_CDATA_START; + } else{ + return T_ERROR; + } + break; default: /* error */ return T_ERROR; @@ -271,12 +304,14 @@ int lexer_get_token(char * tok, int tok_size) { break; /* T_TI_STOP */ - case 9: + case STATE_T_TI_STOP: if (c == '>') { tok[tok_pos] = c; lexbuf_pos++; tok_pos++; /* FIXME */ tok[tok_pos] = '\0'; + if (!in_comment) + lex_mode = DATA; return T_TI_STOP; } else { tok[tok_pos] = '\0'; @@ -285,24 +320,24 @@ int lexer_get_token(char * tok, int tok_size) { break; /* -- */ - case 10: + case STATE_T_DASHDASH: switch (c) { case '-': tok[tok_pos] = c; tok_pos++; lexbuf_pos++; - state = 11; + state = STATE_T_C_STOP; break; default: tok[tok_pos] = c; tok_pos++; lexbuf_pos++; - state = 100; + state = STATE_IDENT; } break; /* --> */ - case 11: + case STATE_T_C_STOP: switch (c) { case '>': tok[tok_pos] = c; @@ -322,12 +357,12 @@ int lexer_get_token(char * tok, int tok_size) { tok[tok_pos] = c; tok_pos++; lexbuf_pos++; - state = 100; + state = STATE_IDENT; } break; /* T_STRING (single quotes) */ - case 12: + case STATE_T_STRING_SINGLE: tok[tok_pos] = c; lexbuf_pos++; if (c == '\'') { /* " */ @@ -338,7 +373,7 @@ int lexer_get_token(char * tok, int tok_size) { break; /* IDENT */ - case 100: + case STATE_IDENT: switch (c) { case '<': case '>': @@ -355,13 +390,13 @@ int lexer_get_token(char * tok, int tok_size) { tok[tok_pos] = c; tok_pos++; lexbuf_pos++; - state = 9; + state = STATE_T_TI_STOP; break; case '-': tok[tok_pos] = c; tok_pos++; lexbuf_pos++; - state = 10; + state = STATE_T_DASHDASH; break; default: tok[tok_pos] = c; @@ -373,8 +408,9 @@ int lexer_get_token(char * tok, int tok_size) { lprintf("expected char \'%c\'\n", tok[tok_pos - 1]); /* FIX ME */ return T_ERROR; } - } else { - /* data mode, stop if char equal '<' */ + break; + + case DATA: /* data mode, stop if char equal '<' */ switch (c) { case '<': @@ -386,6 +422,28 @@ int lexer_get_token(char * tok, int tok_size) { tok_pos++; lexbuf_pos++; } + break; + + case CDATA: /* cdata mode, stop if next token is "]]>" */ + switch (c) + { + case ']': + if (strncmp(lexbuf + lexbuf_pos, "]]>", 3) == 0) { + lexbuf_pos += 3; + lex_mode = DATA; + return T_CDATA_STOP; + } else { + tok[tok_pos] = c; + tok_pos++; + lexbuf_pos++; + } + break; + default: + tok[tok_pos] = c; + tok_pos++; + lexbuf_pos++; + } + break; } } lprintf ("loop done tok_pos = %d, tok_size=%d, lexbuf_pos=%d, lexbuf_size=%d\n", diff --git a/src/xine-utils/xmllexer.h b/src/xine-utils/xmllexer.h index 1e646f9b5..10bcc8676 100644 --- a/src/xine-utils/xmllexer.h +++ b/src/xine-utils/xmllexer.h @@ -47,6 +47,8 @@ #define T_TI_STOP 15 /* ?> */ #define T_DOCTYPE_START 16 /* <!DOCTYPE */ #define T_DOCTYPE_STOP 17 /* > */ +#define T_CDATA_START 18 /* <![CDATA[ */ +#define T_CDATA_STOP 19 /* ]]> */ /* public functions */ diff --git a/src/xine-utils/xmlparser.c b/src/xine-utils/xmlparser.c index a5d8212d2..a7bc146a9 100644 --- a/src/xine-utils/xmlparser.c +++ b/src/xine-utils/xmlparser.c @@ -19,6 +19,10 @@ * Floor, Boston, MA 02110, USA */ +#ifdef XINE_COMPILE +# include "config.h" +#endif + #include <unistd.h> #include <stdio.h> #include <stdlib.h> @@ -73,8 +77,11 @@ static xml_node_t * new_xml_node(void) { return new_node; } +static const char cdata[] = CDATA_MARKER; + static void free_xml_node(xml_node_t * node) { - free (node->name); + if (node->name != cdata) + free (node->name); free (node->data); free(node); } @@ -149,18 +156,79 @@ void xml_parser_free_tree(xml_node_t *current_node) { xml_parser_free_tree_rec(current_node, 1); } -#define STATE_IDLE 0 -#define STATE_NODE 1 -#define STATE_COMMENT 7 +typedef enum { + /*0*/ + STATE_IDLE, + /* <foo ...> */ + STATE_NODE, + STATE_ATTRIBUTE, + STATE_NODE_CLOSE, + STATE_TAG_TERM, + STATE_ATTRIBUTE_EQUALS, + STATE_STRING, + STATE_TAG_TERM_IGNORE, + /* <?foo ...?> */ + STATE_Q_NODE, + STATE_Q_ATTRIBUTE, + STATE_Q_NODE_CLOSE, + STATE_Q_TAG_TERM, + STATE_Q_ATTRIBUTE_EQUALS, + STATE_Q_STRING, + /* Others */ + STATE_COMMENT, + STATE_DOCTYPE, + STATE_CDATA, +} parser_state_t; + +static xml_node_t *xml_parser_append_text (xml_node_t *node, xml_node_t *subnode, const char *text, int flags) +{ + if (!text || !*text) + return subnode; /* empty string -> nothing to do */ + + if ((flags & XML_PARSER_MULTI_TEXT) && subnode) { + /* we have a subtree, so we can't use node->data */ + if (subnode->name == cdata) { + /* most recent node is CDATA - append to it */ + char *newtext; + asprintf (&newtext, "%s%s", subnode->data, text); + free (subnode->data); + subnode->data = newtext; + } else { + /* most recent node is not CDATA - add a sibling */ + subnode->next = new_xml_node (); + subnode->next->name = cdata; + subnode->next->data = strdup (text); + subnode = subnode->next; + } + } else if (node->data) { + /* "no" subtree, but we have existing text - append to it */ + char *newtext; + asprintf (&newtext, "%s%s", node->data, text); + free (node->data); + node->data = newtext; + } else { + /* no text, "no" subtree - duplicate & assign */ + while (isspace (*text)) + ++text; + if (*text) + node->data = strdup (text); + } + + return subnode; +} + +#define Q_STATE(CURRENT,NEW) (STATE_##NEW + state - STATE_##CURRENT) -static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int rec) { +static int xml_parser_get_node_internal (xml_node_t *current_node, char *root_names[], int rec, int flags) +{ char tok[TOKEN_SIZE]; char property_name[TOKEN_SIZE]; char node_name[TOKEN_SIZE]; - int state = STATE_IDLE; + parser_state_t state = STATE_IDLE; int res = 0; int parse_res; int bypass_get_token = 0; + int retval = 0; /* used when state==4; non-0 if there are missing </...> */ xml_node_t *subtree = NULL; xml_node_t *current_subtree = NULL; xml_property_t *current_property = NULL; @@ -182,30 +250,33 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r /* do nothing */ break; case (T_EOF): - return 0; /* normal end */ + return retval; /* normal end */ break; case (T_M_START_1): state = STATE_NODE; break; case (T_M_START_2): - state = 3; + state = STATE_NODE_CLOSE; break; case (T_C_START): state = STATE_COMMENT; break; case (T_TI_START): - state = 8; + state = STATE_Q_NODE; break; case (T_DOCTYPE_START): - state = 9; + state = STATE_DOCTYPE; + break; + case (T_CDATA_START): + state = STATE_CDATA; break; case (T_DATA): /* current data */ - if (current_node->data) { - /* avoid a memory leak */ - free(current_node->data); + { + char *decoded = lexer_decode_entities (tok); + current_subtree = xml_parser_append_text (current_node, current_subtree, decoded, flags); + free (decoded); } - current_node->data = lexer_decode_entities(tok); lprintf("info: node data : %s\n", current_node->data); break; default: @@ -216,6 +287,7 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r break; case STATE_NODE: + case STATE_Q_NODE: switch (res) { case (T_IDENT): properties = NULL; @@ -225,8 +297,13 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r if (xml_parser_mode == XML_PARSER_CASE_INSENSITIVE) { strtoupper(tok); } - strcpy(node_name, tok); - state = 2; + if (state == STATE_Q_NODE) { + snprintf (node_name, TOKEN_SIZE, "?%s", tok); + state = STATE_Q_ATTRIBUTE; + } else { + strcpy(node_name, tok); + state = STATE_ATTRIBUTE; + } lprintf("info: current node name \"%s\"\n", node_name); break; default: @@ -235,7 +312,8 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r break; } break; - case 2: + + case STATE_ATTRIBUTE: switch (res) { case (T_EOL): case (T_SEPAR): @@ -251,8 +329,9 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r /* set node propertys */ subtree->props = properties; lprintf("info: rec %d new subtree %s\n", rec, node_name); - parse_res = xml_parser_get_node(subtree, node_name, rec + 1); - if (parse_res != 0) { + root_names[rec + 1] = node_name; + parse_res = xml_parser_get_node_internal(subtree, root_names, rec + 1, flags); + if (parse_res == -1 || parse_res > 0) { return parse_res; } if (current_subtree == NULL) { @@ -262,11 +341,16 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r current_subtree->next = subtree; current_subtree = subtree; } + if (parse_res < -1) { + /* badly-formed XML (missing close tag) */ + return parse_res + 1 + (parse_res == -2); + } state = STATE_IDLE; break; case (T_M_STOP_2): /* new leaf */ /* new subtree */ + new_leaf: subtree = new_xml_node(); /* set node name */ @@ -288,11 +372,12 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r break; case (T_IDENT): /* save property name */ + new_prop: if (xml_parser_mode == XML_PARSER_CASE_INSENSITIVE) { strtoupper(tok); } strcpy(property_name, tok); - state = 5; + state = Q_STATE(ATTRIBUTE, ATTRIBUTE_EQUALS); lprintf("info: current property name \"%s\"\n", property_name); break; default: @@ -302,17 +387,50 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r } break; - case 3: + case STATE_Q_ATTRIBUTE: + switch (res) { + case (T_EOL): + case (T_SEPAR): + /* nothing */ + break; + case (T_TI_STOP): + goto new_leaf; + case (T_IDENT): + goto new_prop; + default: + lprintf("error: unexpected token \"%s\", state %d\n", tok, state); + return -1; + break; + } + break; + + case STATE_NODE_CLOSE: switch (res) { case (T_IDENT): /* must be equal to root_name */ if (xml_parser_mode == XML_PARSER_CASE_INSENSITIVE) { strtoupper(tok); } - if (strcmp(tok, root_name) == 0) { - state = 4; - } else { - lprintf("error: xml struct, tok=%s, waited_tok=%s\n", tok, root_name); + if (strcmp(tok, root_names[rec]) == 0) { + state = STATE_TAG_TERM; + } else if (flags & XML_PARSER_RELAXED) { + int r = rec; + while (--r >= 0) + if (strcmp(tok, root_names[r]) == 0) { + lprintf("warning: wanted %s, got %s - assuming missing close tags\n", root_names[rec], tok); + retval = r - rec - 1; /* -1 - (no. of implied close tags) */ + state = STATE_TAG_TERM; + break; + } + /* relaxed parsing, ignoring extra close tag (but we don't handle out-of-order) */ + if (r < 0) { + lprintf("warning: extra close tag %s - ignoring\n", tok); + state = STATE_TAG_TERM_IGNORE; + } + } + else + { + lprintf("error: xml struct, tok=%s, waited_tok=%s\n", tok, root_names[rec]); return -1; } break; @@ -324,10 +442,10 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r break; /* > expected */ - case 4: + case STATE_TAG_TERM: switch (res) { case (T_M_STOP_1): - return 0; + return retval; break; default: lprintf("error: unexpected token \"%s\", state %d\n", tok, state); @@ -337,18 +455,18 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r break; /* = or > or ident or separator expected */ - case 5: + case STATE_ATTRIBUTE_EQUALS: switch (res) { case (T_EOL): case (T_SEPAR): /* do nothing */ break; case (T_EQUAL): - state = 6; + state = STATE_STRING; break; case (T_IDENT): bypass_get_token = 1; /* jump to state 2 without get a new token */ - state = 2; + state = STATE_ATTRIBUTE; break; case (T_M_STOP_1): /* add a new property without value */ @@ -362,7 +480,42 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r current_property->name = strdup (property_name); lprintf("info: new property %s\n", current_property->name); bypass_get_token = 1; /* jump to state 2 without get a new token */ - state = 2; + state = STATE_ATTRIBUTE; + break; + default: + lprintf("error: unexpected token \"%s\", state %d\n", tok, state); + return -1; + break; + } + break; + + /* = or ?> or ident or separator expected */ + case STATE_Q_ATTRIBUTE_EQUALS: + switch (res) { + case (T_EOL): + case (T_SEPAR): + /* do nothing */ + break; + case (T_EQUAL): + state = STATE_Q_STRING; + break; + case (T_IDENT): + bypass_get_token = 1; /* jump to state 2 without get a new token */ + state = STATE_Q_ATTRIBUTE; + break; + case (T_TI_STOP): + /* add a new property without value */ + if (current_property == NULL) { + properties = new_xml_property(); + current_property = properties; + } else { + current_property->next = new_xml_property(); + current_property = current_property->next; + } + current_property->name = strdup (property_name); + lprintf("info: new property %s\n", current_property->name); + bypass_get_token = 1; /* jump to state 2 without get a new token */ + state = STATE_Q_ATTRIBUTE; break; default: lprintf("error: unexpected token \"%s\", state %d\n", tok, state); @@ -372,7 +525,8 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r break; /* string or ident or separator expected */ - case 6: + case STATE_STRING: + case STATE_Q_STRING: switch (res) { case (T_EOL): case (T_SEPAR): @@ -391,7 +545,7 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r current_property->name = strdup(property_name); current_property->value = lexer_decode_entities(tok); lprintf("info: new property %s=%s\n", current_property->name, current_property->value); - state = 2; + state = Q_STATE(STRING, ATTRIBUTE); break; default: lprintf("error: unexpected token \"%s\", state %d\n", tok, state); @@ -407,31 +561,45 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r state = STATE_IDLE; break; default: - state = STATE_COMMENT; break; } break; - /* ?> expected */ - case 8: + /* > expected */ + case STATE_DOCTYPE: switch (res) { - case (T_TI_STOP): + case (T_M_STOP_1): state = 0; break; default: - state = 8; break; } break; - /* > expected */ - case 9: + /* ]]> expected */ + case STATE_CDATA: + switch (res) { + case (T_CDATA_STOP): + current_subtree = xml_parser_append_text (current_node, current_subtree, tok, flags); + lprintf("info: node cdata : %s\n", tok); + state = STATE_IDLE; + break; + default: + lprintf("error: unexpected token \"%s\", state %d\n", tok, state); + return -1; + break; + } + break; + + /* > expected (following unmatched "</...") */ + case STATE_TAG_TERM_IGNORE: switch (res) { case (T_M_STOP_1): - state = 0; + state = STATE_IDLE; break; default: - state = 9; + lprintf("error: unexpected token \"%s\", state %d\n", tok, state); + return -1; break; } break; @@ -452,14 +620,51 @@ static int xml_parser_get_node (xml_node_t *current_node, char *root_name, int r } } -int xml_parser_build_tree(xml_node_t **root_node) { - xml_node_t *tmp_node; +static int xml_parser_get_node (xml_node_t *current_node, int flags) +{ + char *root_names[MAX_RECURSION + 1]; + root_names[0] = ""; + return xml_parser_get_node_internal (current_node, root_names, 0, flags); +} + +int xml_parser_build_tree_with_options(xml_node_t **root_node, int flags) { + xml_node_t *tmp_node, *pri_node, *q_node; int res; tmp_node = new_xml_node(); - res = xml_parser_get_node(tmp_node, "", 0); - if ((tmp_node->child) && (!tmp_node->child->next)) { - *root_node = tmp_node->child; + res = xml_parser_get_node(tmp_node, flags); + + /* delete any top-level [CDATA] nodes */; + pri_node = tmp_node->child; + q_node = NULL; + while (pri_node) { + if (pri_node->name == cdata) { + xml_node_t *old = pri_node; + if (q_node) + q_node->next = pri_node->next; + else + q_node = pri_node; + pri_node = pri_node->next; + free_xml_node (old); + } else { + q_node = pri_node; + pri_node = pri_node->next; + } + } + + /* find first non-<?...?> node */; + for (pri_node = tmp_node->child, q_node = NULL; + pri_node && pri_node->name[0] == '?'; + pri_node = pri_node->next) + q_node = pri_node; /* last <?...?> node (eventually), or NULL */ + + if (pri_node && !pri_node->next) { + /* move the tail to the head (for compatibility reasons) */ + if (q_node) { + pri_node->next = tmp_node->child; + q_node->next = NULL; + } + *root_node = pri_node; free_xml_node(tmp_node); res = 0; } else { @@ -470,6 +675,10 @@ int xml_parser_build_tree(xml_node_t **root_node) { return res; } +int xml_parser_build_tree(xml_node_t **root_node) { + return xml_parser_build_tree_with_options (root_node, 0); +} + const char *xml_parser_get_property (const xml_node_t *node, const char *name) { xml_property_t *prop; @@ -588,5 +797,8 @@ static void xml_parser_dump_node (const xml_node_t *node, int indent) { } void xml_parser_dump_tree (const xml_node_t *node) { - xml_parser_dump_node (node, 0); + do { + xml_parser_dump_node (node, 0); + node = node->next; + } while (node); } diff --git a/src/xine-utils/xmlparser.h b/src/xine-utils/xmlparser.h index 98695a756..c89cb6dd3 100644 --- a/src/xine-utils/xmlparser.h +++ b/src/xine-utils/xmlparser.h @@ -34,6 +34,12 @@ #define XML_PARSER_OK 0 #define XML_PARSER_ERROR 1 +/* xml_parser_build_tree_with_options flag bits */ +#define XML_PARSER_RELAXED 1 +#define XML_PARSER_MULTI_TEXT 2 + +/* node name for extra text chunks */ +#define CDATA_MARKER "[CDATA]" /* xml property */ typedef struct xml_property_s { @@ -43,6 +49,14 @@ typedef struct xml_property_s { } xml_property_t; /* xml node */ +/* .data contains any text which precedes any subtree elements; + * subtree elements may also contain only text; if so, name is "[CDATA]". + * e.g. <a>b<c />d</a> + * node1: .name="a" .data="b" .child=node2 .next=NULL + * node2: .name="c" .data=NULL .child=NULL .next=node3 + * node3: .name="[CDATA]" .data="d" .child=NULL .next=NULL + * Adjacent text items are merged. + */ typedef struct xml_node_s { char *name; char *data; @@ -54,6 +68,7 @@ typedef struct xml_node_s { void xml_parser_init(const char * buf, int size, int mode) XINE_PROTECTED; int xml_parser_build_tree(xml_node_t **root_node) XINE_PROTECTED; +int xml_parser_build_tree_with_options(xml_node_t **root_node, int flags) XINE_PROTECTED; void xml_parser_free_tree(xml_node_t *root_node) XINE_PROTECTED; |