/** * @file xmltopyx.c * @brief Transforms XML documents into the PYX format. * * @author Juan M. Bello Rivas * * To compile, follow these steps: * gcc -Wall -g -O2 `xml2-config --cflags` -c xmltopyx.c * gcc xmltopyx.o -o xmltopyx `xml2-config --libs` */ #include #include #include #include #include #include #include #include #include #define PROG_NAME "xmltopyx" static void usage(void) __attribute__ ((noreturn)); static int process_nodes(xmlTextReaderPtr reader, FILE *fp); static void process_node(xmlTextReaderPtr reader, FILE *fp); static void escape(const xmlChar *text, FILE *fp); int main(int argc, char *argv[]) { int status; int c, validate = 0; xmlTextReaderPtr reader; FILE *fp, *out = stdout; /* if (argc < 2) usage(); */ while ((c = getopt(argc, argv, "vo:h")) != -1) { switch (c) { case 'v': validate = 1; break; case 'o': out = fopen(optarg, "w"); if (!out) { fprintf(stderr, PROG_NAME ": %s\n", strerror(errno)); exit(EXIT_FAILURE); } break; case 'h': default: usage(); break; } } if (optind < argc) fp = fopen(argv[optind++], "r"); else fp = stdin; if (!fp) { fprintf(stderr, PROG_NAME ": %s\n", strerror(errno)); exit(EXIT_FAILURE); } reader = xmlReaderForFd(fileno(fp), NULL, NULL, XML_PARSE_NONET); if (!reader) { fprintf(stderr, PROG_NAME ": Unable to set up XML reader.\n"); fclose(fp); fclose(out); exit(EXIT_FAILURE); } status = xmlTextReaderSetParserProp(reader, XML_PARSER_SUBST_ENTITIES, 1); if (validate) status += xmlTextReaderSetParserProp(reader, XML_PARSER_VALIDATE, 1); assert(status == 0); status = process_nodes(reader, out); if (status == -1) { xmlFreeTextReader(reader); fclose(fp); fclose(out); exit(EXIT_FAILURE); } xmlFreeTextReader(reader); fclose(fp); fclose(out); exit(EXIT_SUCCESS); } void usage(void) { fprintf(stderr, PROG_NAME " -- Translates XML documents to the PYX format\n\n" "Usage: " PROG_NAME " [ -v ] [ -o output ] [ input ]\n\n" "Examples:\n" " " PROG_NAME "\n" "\tTranslates a document from stdin without validation\n\n" " " PROG_NAME " -v file.xml\n" "\tValidates and translates file.xml\n\n" " " PROG_NAME " -o file.pyx file.xml\n" "\tTranslates file.xml and writes the output in file.pyx\n" ); exit(EXIT_FAILURE); } int process_nodes(xmlTextReaderPtr reader, FILE *fp) { int status; for ( ; ; ) { status = xmlTextReaderRead(reader); if (status != 1) return status; process_node(reader, fp); } } static void process_attrs(xmlTextReaderPtr reader, FILE *fp) { int status; for ( ; ; ) { status = xmlTextReaderMoveToNextAttribute(reader); if (status <= 0) break; process_node(reader, fp); } } void process_node(xmlTextReaderPtr reader, FILE *fp) { int is_empty; const xmlChar *name, *value; name = xmlTextReaderConstName(reader); value = xmlTextReaderConstValue(reader); switch (xmlTextReaderNodeType(reader)) { case XML_READER_TYPE_ELEMENT: assert(name); fprintf(fp, "(%s\n", (char *) name); is_empty = xmlTextReaderIsEmptyElement(reader); assert(is_empty != -1); process_attrs(reader, fp); if (is_empty) fprintf(fp, ")%s\n", (char *) name); break; case XML_READER_TYPE_END_ELEMENT: assert(name); fprintf(fp, ")%s\n", (char *) name); break; case XML_READER_TYPE_ATTRIBUTE: assert(name && value); fprintf(fp, "A%s ", name); escape(value, fp); fprintf(fp, "\n"); break; case XML_READER_TYPE_TEXT: case XML_READER_TYPE_CDATA: case XML_READER_TYPE_WHITESPACE: case XML_READER_TYPE_SIGNIFICANT_WHITESPACE: assert(value); fprintf(fp, "-"); escape(value, fp); fprintf(fp, "\n"); break; case XML_READER_TYPE_PROCESSING_INSTRUCTION: assert(name && value); fprintf(fp, "?%s ", name); escape(value, fp); fprintf(fp, "\n"); break; } } void escape(const xmlChar *text, FILE *fp) { int status; const xmlChar *p; for (p = text; *p != '\0'; *p++) { switch (*p) { case '\r': break; case '\n': fprintf(fp, "\\n"); break; case '\t': fprintf(fp, "\\t"); break; case '\\': fprintf(fp, "\\\\"); break; default: status = fprintf(fp, "%c", *p); assert(status != EOF); } } }