📄 xtag.c
字号:
/***************************************************************************** * xlist.c : a trivial parser for XML-like tags ***************************************************************************** * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research * Organisation (CSIRO) Australia * Copyright (C) 2000-2004 VideoLAN * * $Id: xtag.c 10101 2005-03-02 16:47:31Z robux4 $ * * Authors: Conrad Parker <Conrad.Parker@csiro.au> * Andre Pang <Andre.Pang@csiro.au> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. *****************************************************************************/#include "config.h"#include <ctype.h>#include <string.h>#include <stdarg.h>#include <stdio.h>#include <stdlib.h>#include <xlist.h>#undef XTAG_DEBUG#undef FALSE#undef TRUE#define FALSE (0)#define TRUE (!FALSE)#undef MIN#define MIN(a,b) ((a)<(b)?(a):(b))#undef MAX#define MAX(a,b) ((a)>(b)?(a):(b))typedef struct _XTag XTag;typedef struct _XAttribute XAttribute;typedef struct _XTagParser XTagParser;/* * struct _XTag is kind of a union ... it normally represents a whole * tag (and its children), but it could alternatively represent some * PCDATA. Basically, if tag->pcdata is non-NULL, interpret only it and * ignore the name, attributes and inner_tags. */struct _XTag { char * name; char * pcdata; XTag * parent; XList * attributes; XList * children; XList * current_child;};struct _XAttribute { char * name; char * value;};struct _XTagParser { int valid; /* boolean */ XTag * current_tag; char * start; char * end;};/* Character classes */#define X_NONE 0#define X_WHITESPACE 1<<0#define X_OPENTAG 1<<1#define X_CLOSETAG 1<<2#define X_DQUOTE 1<<3#define X_SQUOTE 1<<4#define X_EQUAL 1<<5#define X_SLASH 1<<6static intxtag_cin (char c, int char_class){ if (char_class & X_WHITESPACE) if (isspace(c)) return TRUE; if (char_class & X_OPENTAG) if (c == '<') return TRUE; if (char_class & X_CLOSETAG) if (c == '>') return TRUE; if (char_class & X_DQUOTE) if (c == '"') return TRUE; if (char_class & X_SQUOTE) if (c == '\'') return TRUE; if (char_class & X_EQUAL) if (c == '=') return TRUE; if (char_class & X_SLASH) if (c == '/') return TRUE; return FALSE;}static intxtag_index (XTagParser * parser, int char_class){ char * s; int i; s = parser->start; for (i = 0; s[i] && s != parser->end; i++) { if (xtag_cin(s[i], char_class)) return i; } return -1;}static voidxtag_skip_over (XTagParser * parser, int char_class){ char * s; int i; if (!parser->valid) return; s = (char *)parser->start; for (i = 0; s[i] && s != parser->end; i++) { if (!xtag_cin(s[i], char_class)) { parser->start = &s[i]; return; } } return;}static voidxtag_skip_whitespace (XTagParser * parser){ xtag_skip_over (parser, X_WHITESPACE);}#if 0static voidxtag_skip_to (XTagParser * parser, int char_class){ char * s; int i; if (!parser->valid) return; s = (char *)parser->start; for (i = 0; s[i] && s != parser->end; i++) { if (xtag_cin(s[i], char_class)) { parser->start = &s[i]; return; } } return; }#endifstatic char *xtag_slurp_to (XTagParser * parser, int good_end, int bad_end){ char * s, * ret; int xi; if (!parser->valid) return NULL; s = parser->start; xi = xtag_index (parser, good_end | bad_end); if (xi > 0 && xtag_cin (s[xi], good_end)) { ret = malloc ((xi+1) * sizeof(char)); strncpy (ret, s, xi); ret[xi] = '\0'; parser->start = &s[xi]; return ret; } return NULL;}static intxtag_assert_and_pass (XTagParser * parser, int char_class){ char * s; if (!parser->valid) return FALSE; s = parser->start; if (!xtag_cin (s[0], char_class)) { parser->valid = FALSE; return FALSE; } parser->start = &s[1]; return TRUE;}static char *xtag_slurp_quoted (XTagParser * parser){ char * s, * ret; int quote = X_DQUOTE; /* quote char to match on */ int xi; if (!parser->valid) return NULL; xtag_skip_whitespace (parser); s = parser->start; if (xtag_cin (s[0], X_SQUOTE)) quote = X_SQUOTE; if (!xtag_assert_and_pass (parser, quote)) return NULL; s = parser->start; for (xi = 0; s[xi]; xi++) { if (xtag_cin (s[xi], quote)) { if (!(xi > 1 && s[xi-1] == '\\')) break; } } ret = malloc ((xi+1) * sizeof(char)); strncpy (ret, s, xi); ret[xi] = '\0'; parser->start = &s[xi]; if (!xtag_assert_and_pass (parser, quote)) return NULL; return ret;}static XAttribute *xtag_parse_attribute (XTagParser * parser){ XAttribute * attr; char * name, * value; char * s; if (!parser->valid) return NULL; xtag_skip_whitespace (parser); name = xtag_slurp_to (parser, X_WHITESPACE | X_EQUAL, X_SLASH | X_CLOSETAG); if (name == NULL) return NULL; xtag_skip_whitespace (parser); s = parser->start; if (!xtag_assert_and_pass (parser, X_EQUAL)) {#ifdef XTAG_DEBUG printf ("xtag: attr failed EQUAL on <%s>\n", name);#endif goto err_free_name; } xtag_skip_whitespace (parser); value = xtag_slurp_quoted (parser); if (value == NULL) {#ifdef XTAG_DEBUG printf ("Got NULL quoted attribute value\n");#endif goto err_free_name; } attr = malloc (sizeof (*attr)); attr->name = name; attr->value = value; return attr; err_free_name: free (name); parser->valid = FALSE; return NULL;}static XTag *xtag_parse_tag (XTagParser * parser){ XTag * tag, * inner; XAttribute * attr; char * name; char * pcdata; char * s; if (!parser->valid) return NULL; if ((pcdata = xtag_slurp_to (parser, X_OPENTAG, X_NONE)) != NULL) { tag = malloc (sizeof (*tag)); tag->name = NULL; tag->pcdata = pcdata; tag->parent = parser->current_tag; tag->attributes = NULL; tag->children = NULL; tag->current_child = NULL; return tag; } s = parser->start; /* if this starts a close tag, return NULL and let the parent take it */ if (xtag_cin (s[0], X_OPENTAG) && xtag_cin (s[1], X_SLASH)) return NULL; if (!xtag_assert_and_pass (parser, X_OPENTAG)) return NULL; name = xtag_slurp_to (parser, X_WHITESPACE | X_SLASH | X_CLOSETAG, X_NONE); if (name == NULL) return NULL;#ifdef XTAG_DEBUG printf ("<%s ...\n", name);#endif tag = malloc (sizeof (*tag)); tag->name = name; tag->pcdata = NULL; tag->parent = parser->current_tag; tag->attributes = NULL; tag->children = NULL; tag->current_child = NULL; s = parser->start; if (xtag_cin (s[0], X_WHITESPACE)) { while ((attr = xtag_parse_attribute (parser)) != NULL) { tag->attributes = xlist_append (tag->attributes, attr); } } xtag_skip_whitespace (parser); s = parser->start; if (xtag_cin (s[0], X_CLOSETAG)) { parser->current_tag = tag; xtag_assert_and_pass (parser, X_CLOSETAG); while ((inner = xtag_parse_tag (parser)) != NULL) { tag->children = xlist_append (tag->children, inner); } xtag_skip_whitespace (parser); xtag_assert_and_pass (parser, X_OPENTAG); xtag_assert_and_pass (parser, X_SLASH); name = xtag_slurp_to (parser, X_WHITESPACE | X_CLOSETAG, X_NONE); if (name) { if (name && tag->name && strcmp (name, tag->name)) {#ifdef XTAG_DEBUG printf ("got %s expected %s\n", name, tag->name);#endif parser->valid = FALSE; } free (name); } xtag_skip_whitespace (parser); xtag_assert_and_pass (parser, X_CLOSETAG); } else { xtag_assert_and_pass (parser, X_SLASH); xtag_assert_and_pass (parser, X_CLOSETAG); } return tag;}XTag *xtag_free (XTag * xtag){ XList * l; XAttribute * attr; XTag * child; if (xtag == NULL) return NULL; if (xtag->name) free (xtag->name); if (xtag->pcdata) free (xtag->pcdata); for (l = xtag->attributes; l; l = l->next) { if ((attr = (XAttribute *)l->data) != NULL) { if (attr->name) free (attr->name); if (attr->value) free (attr->value); free (attr); } } xlist_free (xtag->attributes); for (l = xtag->children; l; l = l->next) { child = (XTag *)l->data; xtag_free (child); } xlist_free (xtag->children); free (xtag); return NULL;}XTag *xtag_new_parse (const char * s, int n){ XTagParser parser; XTag * tag, * ttag, * wrapper; parser.valid = TRUE; parser.current_tag = NULL; parser.start = (char *)s; if (n == -1) parser.end = NULL; else if (n == 0) return NULL; else parser.end = (char *)&s[n]; tag = xtag_parse_tag (&parser); if (!parser.valid) { xtag_free (tag); return NULL; } if ((ttag = xtag_parse_tag (&parser)) != NULL) { if (!parser.valid) { xtag_free (ttag); return tag; } wrapper = malloc (sizeof (XTag)); wrapper->name = NULL; wrapper->pcdata = NULL; wrapper->parent = NULL; wrapper->attributes = NULL; wrapper->children = NULL; wrapper->current_child = NULL; wrapper->children = xlist_append (wrapper->children, tag); wrapper->children = xlist_append (wrapper->children, ttag); while ((ttag = xtag_parse_tag (&parser)) != NULL) { if (!parser.valid) { xtag_free (ttag); return wrapper; } wrapper->children = xlist_append (wrapper->children, ttag); } return wrapper; } return tag;}char *xtag_get_name (XTag * xtag){ return xtag ? xtag->name : NULL;}char *xtag_get_pcdata (XTag * xtag){ XList * l; XTag * child; if (xtag == NULL) return NULL; for (l = xtag->children; l; l = l->next) { child = (XTag *)l->data; if (child->pcdata != NULL) { return child->pcdata; } } return NULL;}char *xtag_get_attribute (XTag * xtag, char * attribute){ XList * l; XAttribute * attr; if (xtag == NULL) return NULL; for (l = xtag->attributes; l; l = l->next) { if ((attr = (XAttribute *)l->data) != NULL) { if (attr->name && attribute && !strcmp (attr->name, attribute)) return attr->value; } } return NULL;}XTag *xtag_first_child (XTag * xtag, char * name){ XList * l; XTag * child; if (xtag == NULL) return NULL; if ((l = xtag->children) == NULL) return NULL; if (name == NULL) { xtag->current_child = l; return (XTag *)l->data; } for (; l; l = l->next) { child = (XTag *)l->data; if (child->name && name && !strcmp(child->name, name)) { xtag->current_child = l; return child; } } xtag->current_child = NULL; return NULL;}XTag *xtag_next_child (XTag * xtag, char * name){ XList * l; XTag * child; if (xtag == NULL) return NULL; if ((l = xtag->current_child) == NULL) return xtag_first_child (xtag, name); if ((l = l->next) == NULL) return NULL; if (name == NULL) { xtag->current_child = l; return (XTag *)l->data; } for (; l; l = l->next) { child = (XTag *)l->data; if (child->name && name && !strcmp(child->name, name)) { xtag->current_child = l; return child; } } xtag->current_child = NULL; return NULL;}/* * This snprints function takes a variable list of char *, the last of * which must be NULL, and prints each in turn to buf. * Returns C99-style total length that would have been written, even if * this is larger than n. */static intxtag_snprints (char * buf, int n, ...){ va_list ap; char * s; int len, to_copy, total = 0; va_start (ap, n); for (s = va_arg (ap, char *); s; s = va_arg (ap, char *)) { len = strlen (s); if ((to_copy = MIN (n, len)) > 0) { memcpy (buf, s, to_copy); buf += to_copy; n -= to_copy; } total += len; } va_end (ap); return total;}intxtag_snprint (char * buf, int n, XTag * xtag){ int nn, written = 0; XList * l; XAttribute * attr; XTag * child;#define FORWARD(N) \ buf += MIN (n, N); \ n = MAX (n-N, 0); \ written += N; if (xtag == NULL) { if (n > 0) buf[0] = '\0'; return 0; } if (xtag->pcdata) { nn = xtag_snprints (buf, n, xtag->pcdata, NULL); FORWARD(nn); return written; } if (xtag->name) { nn = xtag_snprints (buf, n, "<", xtag->name, NULL); FORWARD(nn); for (l = xtag->attributes; l; l = l->next) { attr = (XAttribute *)l->data; nn = xtag_snprints (buf, n, " ", attr->name, "=\"", attr->value, "\"", NULL); FORWARD(nn); } if (xtag->children == NULL) { nn = xtag_snprints (buf, n, "/>", NULL); FORWARD(nn); return written; } nn = xtag_snprints (buf, n, ">", NULL); FORWARD(nn); } for (l = xtag->children; l; l = l->next) { child = (XTag *)l->data; nn = xtag_snprint (buf, n, child); FORWARD(nn); } if (xtag->name) { nn = xtag_snprints (buf, n, "</", xtag->name, ">", NULL); FORWARD(nn); } return written;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -