/* * (c) 2020 Vincenzo "KatolaZ" Nicosia * * A simple xml-to-tsv converter, based on xmlparser by Hiltjo Posthuma * http://codemadness.org/git/xmlparser/ * * You can use, distribute, modify, and/or redistribute this program under * the terms of the ISC LICENSE. See LICENSE for details. * */ #include #include #include #include #include "xml.h" #include "config.h" #define FALSE 0 #define TRUE 1 /* tag stack */ typedef struct { char s[STR_MAX]; int ref; } taginfo_t; typedef struct { int top; taginfo_t st[DEPTH_MAX]; } tstack_t; int stack_empty(tstack_t *t){ return (t->top < 0); } int stack_push(tstack_t *t, const char *c){ if (t->top < DEPTH_MAX){ t->top ++; strncpy(t->st[t->top].s, c, STR_MAX - 1); t->st[t->top].s[STR_MAX - 1] = '\0'; t->st[t->top].ref = 0; if (t->top){ /* Increment the ref counter of the parent tag*/ t->st[t->top-1].ref += 1; } return 0; } return -1; } taginfo_t* stack_pop(tstack_t *t){ if (!stack_empty(t)) return t->st + t->top--; return NULL; } taginfo_t* stack_peek(tstack_t *t){ if (!stack_empty(t)) return t->st + t->top; return NULL; } void stack_init(tstack_t *t){ t->top = -1; } /* utility functions */ /* quote_print: quote \\, \n, \t, and strip other ctrl chars */ void quote_print(const char *s){ const char *tmp = s; size_t len; int i; while (*tmp != '\0'){ len = strcspn(tmp, "\\\n\t"); for(i=0; i 0){ fputs("\\n", stdout); } tmp ++; break; case '\t': fputs("\\t", stdout); tmp ++; break; case '\r': fputs("\\r", stdout); tmp ++; break; case '\\': fputs("\\\\", stdout); tmp ++; break; } } } void print_cur_str(tstack_t *t){ int i; for (i=0; i<=t->top; i++){ putchar('/'); fputs(t->st[i].s, stdout); } } void print_cur_str_fp(FILE *f, tstack_t *t){ int i; for (i=0; i<=t->top; i++){ fputc('/', f); fputs(t->st[i].s, f); } } /* global variables */ tstack_t st; char emitsep; /* XML callbacks */ void xmlattr(XMLParser *x, const char *t, size_t tl, const char *a, size_t al, const char *v, size_t vl) { fputs(v, stdout); } void xmlattrentity(XMLParser *x, const char *t, size_t tl, const char *a, size_t al, const char *v, size_t vl) { char buf[16]; int n; if ((n = xml_entitytostr(v, buf, sizeof(buf))) > 0) xmlattr(x, t, tl, a, al, buf, (size_t)n); else xmlattr(x, t, tl, a, al, v, vl); } void xmlattrstart(XMLParser *x, const char *t, size_t tl, const char *a, size_t al) { putchar(SEP); fputs(a, stdout); putchar(SATTR); } void xmlcdatastart(XMLParser *x) { putchar(SEP); } void xmlcdata(XMLParser *x, const char *d, size_t dl) { quote_print(d); } void xmldata(XMLParser *x, const char *d, size_t dl) { if (stack_peek(&st) || (strcspn(d, " \t\n") && emitsep)){ putchar(SEP); emitsep = FALSE; } quote_print(d); } void xmldataend(XMLParser *x) { emitsep = FALSE; } void xmldataentity(XMLParser *x, const char *d, size_t dl) { char buf[16]; int n; if ((n = xml_entitytostr(d, buf, sizeof(buf))) > 0) xmldata(x, buf, (size_t)n); else xmldata(x, d, dl); } void xmldatastart(XMLParser *x) { emitsep = TRUE; } void xmltagend(XMLParser *x, const char *t, size_t tl, int isshort) { char *tag; if (stack_empty(&st)){ fprintf(stderr, "Error: tag-end '%s' before any open tag\n", t); } tag = stack_pop(&st)->s; if (strcmp(t, tag)){ fprintf(stderr, "Error: tag-end '%s' closes tag '%s'\n", t, tag); } } void xmltagstart(XMLParser *x, const char *t, size_t tl) { if (stack_push(&st, t)){ fprintf(stderr, "Error: stack full. Ignoring tag '%s' (parent tag: '%s')\n", t, stack_peek(&st)->s); return; } putchar('\n'); print_cur_str(&st); } int main(void) { stack_init(&st); emitsep = FALSE; XMLParser x = { 0 }; x.xmlattr = xmlattr; x.xmlattrstart = xmlattrstart; x.xmlattrentity = xmlattrentity; x.xmlcdatastart = xmlcdatastart; x.xmlcdata = xmlcdata; x.xmldata = xmldata; x.xmldataend = xmldataend; x.xmldataentity = xmldataentity; x.xmldatastart = xmldatastart; x.xmltagend = xmltagend; x.xmltagstart = xmltagstart; xml_parse(&x); putchar('\n'); if (! stack_empty(&st)) { fprintf(stderr, "Error: tags still open at EOF: "); print_cur_str_fp(stderr, &st); fprintf(stderr, "\n"); } return 0; }