/*-
* Copyright (c) 2002 Poul-Henning Kamp
* Copyright (c) 2002 Networks Associates Technology, Inc.
* All rights reserved.
*
* This software was developed for the FreeBSD Project by Poul-Henning Kamp
* and NAI Labs, the Security Research Division of Network Associates, Inc.
* under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
* DARPA CHATS research program.
*
* 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. The names of the authors may not 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/queue.h>
#include <sys/sbuf.h>
#include <err.h>
#include <bsdxml.h>
FILE *fsubs;
struct node {
LIST_HEAD(, node) children;
LIST_ENTRY(node) siblings;
struct node *parent;
const char *name;
struct sbuf *cont;
struct sbuf *key;
char *id;
char *ref;
};
struct mytree {
struct node *top;
struct node *cur;
int indent;
int ignore;
};
struct ref {
LIST_ENTRY(ref) next;
char *k1;
char *k2;
};
LIST_HEAD(, ref) refs = LIST_HEAD_INITIALIZER(refs);
static struct node *
new_node(void)
{
struct node *np;
np = calloc(1, sizeof *np);
np->cont = sbuf_new_auto();
sbuf_clear(np->cont);
np->key = sbuf_new_auto();
sbuf_clear(np->key);
LIST_INIT(&np->children);
return (np);
}
static void
indent(int n)
{
printf("%*.*s", n, n, "");
}
static void
StartElement(void *userData, const char *name, const char **attr)
{
struct mytree *mt;
struct node *np;
int i;
mt = userData;
if (!strcmp(name, "FreeBSD")) {
mt->ignore = 1;
return;
}
mt->ignore = 0;
mt->indent += 2;
np = new_node();
for (i = 0; attr[i]; i += 2) {
if (!strcmp(attr[i], "id"))
np->id = strdup(attr[i+1]);
else if (!strcmp(attr[i], "ref"))
np->ref = strdup(attr[i+1]);
}
np->name = strdup(name);
sbuf_cat(np->key, name);
sbuf_cat(np->key, "::");
np->parent = mt->cur;
LIST_INSERT_HEAD(&mt->cur->children, np, siblings);
mt->cur = np;
}
static void
EndElement(void *userData, const char *name __unused)
{
struct mytree *mt;
struct node *np;
mt = userData;
if (mt->ignore)
return;
mt->indent -= 2;
sbuf_finish(mt->cur->cont);
LIST_FOREACH(np, &mt->cur->children, siblings) {
if (strcmp(np->name, "name"))
continue;
sbuf_cat(mt->cur->key, sbuf_data(np->cont));
break;
}
sbuf_finish(mt->cur->key);
mt->cur = mt->cur->parent;
}
static void
CharData(void *userData , const XML_Char *s , int len)
{
struct mytree *mt;
const char *b, *e;
mt = userData;
if (mt->ignore)
return;
b = s;
e = s + len - 1;
while (isspace(*b) && b < e)
b++;
while (isspace(*e) && e > b)
e--;
if (e != b || *b)
sbuf_bcat(mt->cur->cont, b, e - b + 1);
}
static struct mytree *
dofile(char *filename)
{
XML_Parser parser;
struct mytree *mt;
struct stat st;
int fd;
char *p;
int i;
parser = XML_ParserCreate(NULL);
mt = calloc(1, sizeof *mt);
mt->top = new_node();
mt->top->name = "(top)";
mt->top->parent = mt->top;
mt->cur = mt->top;
sbuf_finish(mt->top->key);
sbuf_finish(mt->top->cont);
XML_SetUserData(parser, mt);
XML_SetElementHandler(parser, StartElement, EndElement);
XML_SetCharacterDataHandler(parser, CharData);
fd = open(filename, O_RDONLY);
if (fd < 0)
err(1, filename);
fstat(fd, &st);
p = mmap(NULL, st.st_size, PROT_READ, MAP_NOCORE|MAP_PRIVATE, fd, 0);
i = XML_Parse(parser, p, st.st_size, 1);
if (i != 1)
errx(1, "XML_Parse complained -> %d", i);
munmap(p, st.st_size);
close(fd);
XML_ParserFree(parser);
sbuf_finish(mt->top->cont);
if (i)
return (mt);
else
return (NULL);
}
static void
print_node(struct node *np)
{
printf("\"%s\" -- \"%s\" -- \"%s\"", np->name, sbuf_data(np->cont), sbuf_data(np->key));
if (np->id)
printf(" id=\"%s\"", np->id);
if (np->ref)
printf(" ref=\"%s\"", np->ref);
printf("\n");
}
static void
print_tree(struct node *np, int n)
{
struct node *np1;
indent(n); printf("%s id=%s ref=%s\n", np->name, np->id, np->ref);
LIST_FOREACH(np1, &np->children, siblings)
print_tree(np1, n + 2);
}
static void
sort_node(struct node *np)
{
struct node *np1, *np2;
int n;
LIST_FOREACH(np1, &np->children, siblings)
sort_node(np1);
do {
np1 = LIST_FIRST(&np->children);
n = 0;
for (;;) {
if (np1 == NULL)
return;
np2 = LIST_NEXT(np1, siblings);
if (np2 == NULL)
return;
if (strcmp(sbuf_data(np1->key), sbuf_data(np2->key)) > 0) {
LIST_REMOVE(np2, siblings);
LIST_INSERT_BEFORE(np1, np2, siblings);
n++;
break;
}
np1 = np2;
}
} while (n);
}
static int
refcmp(char *r1, char *r2)
{
struct ref *r;
LIST_FOREACH(r, &refs, next) {
if (!strcmp(r1, r->k1))
return (strcmp(r2, r->k2));
}
r = calloc(1, sizeof(*r));
r->k1 = strdup(r1);
r->k2 = strdup(r2);
LIST_INSERT_HEAD(&refs, r, next);
if (fsubs != NULL) {
fprintf(fsubs, "s/%s/%s/g\n", r1, r2);
fflush(fsubs);
}
return (0);
}
static int compare_node2(struct node *n1, struct node *n2, int in);
static int
compare_node(struct node *n1, struct node *n2, int in)
{
int i;
struct node *n1a, *n2a;
i = strcmp(n1->name, n2->name);
if (i)
return (i);
if (n1->id && n2->id)
i = refcmp(n1->id, n2->id);
else if (n1->id || n2->id)
i = -1;
if (i)
return (i);
if (n1->ref && n2->ref)
i = refcmp(n1->ref, n2->ref);
else if (n1->ref || n2->ref)
i = -1;
if (i)
return (i);
if (!strcmp(n1->name, "ref"))
i = refcmp(sbuf_data(n1->cont), sbuf_data(n2->cont));
else
i = strcmp(sbuf_data(n1->cont), sbuf_data(n2->cont));
if (i)
return (1);
n1a = LIST_FIRST(&n1->children);
n2a = LIST_FIRST(&n2->children);
for (;;) {
if (n1a == NULL && n2a == NULL)
return (0);
if (n1a != NULL && n2a == NULL) {
printf("1>");
indent(in);
print_node(n1a);
printf("2>\n");
return (1);
}
if (n1a == NULL && n2a != NULL) {
printf("1>\n");
printf("2>");
indent(in);
print_node(n2a);
return (1);
}
i = compare_node2(n1a, n2a, in + 2);
if (i)
return (1);
n1a = LIST_NEXT(n1a, siblings);
n2a = LIST_NEXT(n2a, siblings);
}
return (0);
}
static int
compare_node2(struct node *n1, struct node *n2, int in)
{
int i;
i = compare_node(n1, n2, in);
if (i) {
printf("1>");
indent(in);
print_node(n1);
printf("2>");
indent(in);
print_node(n2);
}
return (i);
}
int
main(int argc, char **argv)
{
struct mytree *t1, *t2;
int i;
fsubs = fopen("_.subs", "w");
setbuf(stdout, NULL);
setbuf(stderr, NULL);
if (argc != 3)
errx(1, "usage: %s file1 file2", argv[0]);
t1 = dofile(argv[1]);
if (t1 == NULL)
errx(2, "XML parser error on file %s", argv[1]);
sort_node(t1->top);
t2 = dofile(argv[2]);
if (t2 == NULL)
errx(2, "XML parser error on file %s", argv[2]);
sort_node(t2->top);
i = compare_node(t1->top, t2->top, 0);
return (i);
}