/* * XML Catalog Manager (xmlcatmgr) * $Id: xml.c,v 1.2 2004/08/31 21:25:47 jmmv Exp $ * * Copyright (c) 2003, 2004 Julio M. Merino Vidal. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of the author nor the names of contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This file implements all XML mode actions and catalog handling * (although not parsing). */ #include "system.h" #ifndef lint __RCSID("$Id: xml.c,v 1.2 2004/08/31 21:25:47 jmmv Exp $"); #endif #include "grstr.h" #include "mem.h" #include "linklist.h" #include "xmldoc.h" #include "xmlnode.h" #include "xml.h" /* This structure matches catalog entries with the number and name of * attributes they require. A value of zero in the t_params fields * means that the specific entry cannot be given in the command line * as an argument to add or remove. */ static struct type { const char *t_name; int t_params; const char *t_origattr; const char *t_replattr; } Types[] = { { "catalog", 0, NULL, NULL }, { "delegatePublic", 2, "publicIdStartString", "catalog" }, { "delegateSystem", 2, "systemIdStartString", "catalog" }, { "delegateURI", 2, "uriStartString", "catalog" }, { "group", 0, NULL, NULL }, { "nextCatalog", 1, "catalog", NULL }, { "public", 2, "publicId", "uri" }, { "rewriteSystem", 2, "systemIdStartString", "rewritePrefix" }, { "rewriteURI", 2, "uriStartString", "rewritePrefix" }, { "system", 2, "systemId", "uri" }, { "uri", 2, "name", "uri" }, { NULL, 0, NULL, NULL } }; static const struct type *get_type_info(const char *); static bool add_entry(struct xmldoc *, const char *, const char *, const char *, bool); static bool remove_entry(struct xmldoc *, const char *, const char *); static struct xmldoc *read_catalog(FILE *); static bool write_catalog(struct xmldoc *, FILE *); static bool validate_catalog(struct xmlnode *); static struct xmlnode *search_entry(struct xmlnode *, const char *, const char *, bool); /* --------------------------------------------------------------------- */ /* * The XML add action. For each triplet of arguments, add_entry is * called to register the given entry in the catalog. */ bool xml_add(int argc, char *const *argv, FILE *f, bool prepend) { bool res; struct xmldoc *catalog; catalog = read_catalog(f); if (catalog == NULL) return false; res = true; while (argc > 0) { const char *type, *orig, *replace; if (argc == 1) { warnx("unbalanced arguments for `add' action"); res = false; argc--; argv++; } else { type = argv[0]; orig = argv[1]; argc -= 2; argv += 2; if (argc >= 1) { replace = strcmp(argv[0], "--") != 0 ? argv[0] : NULL; argc--; argv++; } else replace = NULL; res &= add_entry(catalog, type, orig, replace, prepend); } } write_catalog(catalog, f); xmldoc_free(catalog); return res; } /* --------------------------------------------------------------------- */ /* * The XML create action. Generates an empty catalog with a comment and * the root node in it. */ bool xml_create(FILE *f) { char buf[] = " Created by " PACKAGE_STRING " "; struct xmlattr *xa; struct xmldoc *catalog; struct xmlnode *xn; catalog = xmldoc_new("catalog PUBLIC \"-//OASIS//" "DTD Entity Resolution XML Catalog V1.0//EN\"\n" " \"http://www.oasis-open.org/committees/" "entity/release/1.0/catalog.dtd\""); xn = xmlnode_new(XMLNODE_TYPE_COMMENT, strdup("#COMMENT#")); xmlnode_set_text(xn, strdup(buf)); xmldoc_append_node(catalog, xn); xn = xmlnode_new(XMLNODE_TYPE_ROOT, strdup("catalog")); xa = xmlattr_new(strdup("xmlns"), strdup("urn:oasis:names:tc:entity:xmlns:xml:catalog")); XMLNODE_APPEND_ATTR(xn, xa); xmldoc_append_node(catalog, xn); xmldoc_write(catalog, f); xmldoc_free(catalog); return true; } /* --------------------------------------------------------------------- */ /* * The XML lookup action. Searches the given entries in the catalog * file. Only returns success if all of them were found. */ bool xml_lookup(int argc, char *const *argv, FILE *f) { bool res; struct xmldoc *catalog; assert(argc > 0 && argv != NULL && f != NULL); catalog = read_catalog(f); if (catalog == NULL) return false; res = true; while (argc > 0) { struct xmlnode *xn; xn = search_entry(XMLDOC_ROOT(catalog), NULL, argv[0], true); if (xn == NULL) { warnx("no matching entry for `%s'", argv[0]); res = false; } argc--; argv++; } xmldoc_free(catalog); return res; } /* --------------------------------------------------------------------- */ /* * The XML remove action. Removes all given entries from the catalog. * Arguments are expected to come in pairs, although if only one is * provided, all matching catalog entries are removed (compatibility with * previous versions). */ bool xml_remove(int argc, char *const *argv, FILE *f) { bool res; struct xmldoc *catalog; assert(argv != NULL && f != NULL); catalog = read_catalog(f); if (catalog == NULL) return false; if (argc == 1) { warnx("enabling compatibility mode; removing ALL matching entries"); res = remove_entry(catalog, NULL, argv[0]); } else { res = true; while (argc >= 2 && argc % 2 == 0) { res &= remove_entry(catalog, argv[0], argv[1]); argc -= 2; argv += 2; } if (argc % 2 != 0) { warnx("unbalanced arguments for `remove' action"); res = false; } } write_catalog(catalog, f); xmldoc_free(catalog); return res; } /* --------------------------------------------------------------------- */ /* * Reads the given catalog file, and returns an 'xmldoc' object which * describes it. If errors occur during parsing, returns NULL. */ static struct xmldoc * read_catalog(FILE *f) { struct xmldoc *catalog; catalog = xmldoc_parse(f); if (catalog == NULL) { warnx("errors while parsing catalog; aborting"); catalog = NULL; } else if (!validate_catalog(XMLDOC_ROOT(catalog))) { warnx("catalog contains unknown tags or attributes; aborting"); xmldoc_free(catalog); catalog = NULL; } return catalog; } /* --------------------------------------------------------------------- */ /* * Searches the given type in the 'Types' array, and returns a pointer * to it if found; otherwise returns NULL. */ static const struct type * get_type_info(const char *type) { bool found; int i; i = 0; found = false; while (!found && Types[i].t_name != NULL) { if (strcmp(Types[i].t_name, type) == 0) found = true; else i++; } if (!found) warnx("unknown entry type `%s'", type); return found ? &Types[i] : NULL; } /* --------------------------------------------------------------------- */ /* * Adds the given entry to the catalog file, if it's not already present. * Also checks if the type is valid. 'replace' may be null if the given * type only recognizes one argument. */ static bool add_entry(struct xmldoc *c, const char *type, const char *orig, const char *replace, bool prepend) { const struct type *tdata; struct xmlnode *xn; struct xmlattr *xa; assert(c != NULL && type != NULL && orig != NULL); tdata = get_type_info(type); if (tdata == NULL) return false; if (search_entry(XMLDOC_ROOT(c), type, orig, false) != NULL) { warnx("entry already exists for `%s' of type `%s'", orig, type); return false; } xn = xmlnode_new(XMLNODE_TYPE_ELEMENT, strdup(type)); if (xn == NULL) return false; xa = xmlattr_new(strdup(tdata->t_origattr), strdup(orig)); XMLNODE_APPEND_ATTR(xn, xa); if (replace != NULL) { xa = xmlattr_new(strdup(tdata->t_replattr), strdup(replace)); XMLNODE_APPEND_ATTR(xn, xa); } if (prepend) { XMLNODE_PREPEND_CHILD(XMLDOC_ROOT(c), xn); } else { XMLNODE_APPEND_CHILD(XMLDOC_ROOT(c), xn); } return true; } /* --------------------------------------------------------------------- */ /* * Remove the given entry from the catalog file. If 'type' is null, * removes all matching entries, to be compatible with previous versions. */ static bool remove_entry(struct xmldoc *c, const char *type, const char *orig) { bool found; struct xmlnode *xn; assert(c != NULL && orig != NULL); if (type != NULL) { const struct type *tdata; tdata = get_type_info(type); if (tdata == NULL) return false; } found = false; while ((xn = search_entry(XMLDOC_ROOT(c), type, orig, false)) != NULL) { XMLNODE_DETACH(xn); xmlnode_free(xn); found = true; if (type != NULL) break; } if (!found && type != NULL) warnx("no matching entry for `%s' of type `%s'", orig, type); else if (!found) warnx("no matching entry for `%s' of any type", orig); return found; } /* --------------------------------------------------------------------- */ /* * Recurse the given catalog (in fact, just its root node) and check, for * each element, that their attributes are valid (and are present). */ static bool validate_catalog(struct xmlnode *xn) { bool res; const struct type *tdata; assert(xn != NULL); res = true; tdata = get_type_info(XMLNODE_TAG(xn)); if (tdata == NULL) { res = false; } else { struct xmlattr *aiter; struct xmlnode *niter; if (tdata->t_params > 0) { bool seenorig, seenrepl; seenorig = seenrepl = false; XMLNODE_FOREACH_ATTR(aiter, xn) { if (strcmp(XMLATTR_NAME(aiter), tdata->t_origattr) == 0) seenorig = true; else if (strcmp(XMLATTR_NAME(aiter), tdata->t_replattr) == 0) seenrepl = true; else if (!seenorig && !seenrepl) { warnx("unknown attribute <%s>", XMLATTR_NAME(aiter)); res = false; } } if (!seenorig) { warnx("`%s' entry misses attribute `%s'", XMLNODE_TAG(xn), tdata->t_origattr); res = false; } if (tdata->t_params > 1 && !seenrepl) { warnx("`%s' entry misses attribute `%s'", XMLNODE_TAG(xn), tdata->t_replattr); res = false; } } XMLNODE_FOREACH_CHILD(niter, xn) { res &= validate_catalog(niter); } } return res; } /* --------------------------------------------------------------------- */ /* * Recurses the given node searching for an entry (described by its type * 'tname' and its contents 'name'). If 'tname' is null, only 'name' is * matched. Returns a pointer to the matching node on success, or NULL * in case of failure. * If 'nostop' is true, the function will search for all matching nodes, * printing them to standard output. The returned value will be a * pointer to the last matching node (useless). This is only useful for * the lookup action. */ static struct xmlnode * search_entry(struct xmlnode *xn, const char *tname, const char *name, bool nostop) { const struct type *tdata; struct xmlnode *res; assert(xn != NULL && name != NULL); tdata = get_type_info(XMLNODE_TAG(xn)); assert(tdata != NULL); res = NULL; if (tdata->t_params > 0 && strcmp(xmlnode_get_attr_value(xn, tdata->t_origattr), name) == 0) { if ((tname == NULL) || ((tname != NULL) && (strcmp(tdata->t_name, tname) == 0))) { res = xn; } } else { struct xmlnode *niter; XMLNODE_FOREACH_CHILD(niter, xn) { struct xmlnode *tmp; if ((tmp = search_entry(niter, tname, name, nostop)) != NULL) { res = tmp; if (nostop) { struct xmlattr *aiter; printf("%s", XMLNODE_TAG(res)); XMLNODE_FOREACH_ATTR(aiter, res) { printf(" "); xmlattr_write(aiter, stdout); } printf("\n"); } else break; } } } return res; } /* --------------------------------------------------------------------- */ /* * Write the given catalog to the file. This truncates the stream to * zero bytes before writing anything, to ensure the file contains no * garbage. */ static bool write_catalog(struct xmldoc *xd, FILE *f) { assert(xd != NULL && f != NULL); rewind(f); fflush(f); ftruncate(fileno(f), 0); return xmldoc_write(xd, f); } /* * Local Variables: *** * mode: c *** * c-file-style: "stroustrup" *** * End: *** * vim: syntax=c:expandtab:shiftwidth=4:softtabstop=4 */