xmlcatalog.c 13.5 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * xmlcatalog.c : a small utility program to handle XML catalogs
 *
 * See Copyright for the status of this software.
 *
 * daniel@veillard.com
 */

9
#define IN_LIBXML
10 11 12 13 14 15
#include "libxml.h"

#include <string.h>
#include <stdio.h>
#include <stdarg.h>

16 17 18 19
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

Bjorn Reese's avatar
Bjorn Reese committed
20 21 22 23 24 25 26
#ifdef HAVE_LIBREADLINE
#include <readline/readline.h>
#ifdef HAVE_LIBHISTORY
#include <readline/history.h>
#endif
#endif

27 28 29 30
#include <libxml/xmlmemory.h>
#include <libxml/uri.h>
#include <libxml/catalog.h>
#include <libxml/parser.h>
31
#include <libxml/globals.h>
32 33

static int shell = 0;
34
static int sgml = 0;
35
static int noout = 0;
36
static int create = 0;
37 38
static int add = 0;
static int del = 0;
39
static int convert = 0;
40
static int verbose = 0;
41
static char *filename;
42 43

#ifdef LIBXML_CATALOG_ENABLED
44 45 46

#define XML_SGML_DEFAULT_CATALOG "/etc/sgml/catalog"

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
/************************************************************************
 * 									*
 * 			Shell Interface					*
 * 									*
 ************************************************************************/
/**
 * xmlShellReadline:
 * @prompt:  the prompt value
 *
 * Read a string
 * 
 * Returns a pointer to it or NULL on EOF the caller is expected to
 *     free the returned string.
 */
static char *
xmlShellReadline(const char *prompt) {
#ifdef HAVE_LIBREADLINE
    char *line_read;

    /* Get a line from the user. */
    line_read = readline (prompt);

    /* If the line has any text in it, save it on the history. */
    if (line_read && *line_read)
	add_history (line_read);

    return (line_read);
#else
    char line_read[501];
76 77
    char *ret;
    int len;
78 79 80 81 82 83

    if (prompt != NULL)
	fprintf(stdout, "%s", prompt);
    if (!fgets(line_read, 500, stdin))
        return(NULL);
    line_read[500] = 0;
84 85 86 87 88 89
    len = strlen(line_read);
    ret = (char *) malloc(len + 1);
    if (ret != NULL) {
	memcpy (ret, line_read, len + 1);
    }
    return(ret);
90 91 92 93 94 95 96 97
#endif
}

static void usershell(void) {
    char *cmdline = NULL, *cur;
    int nbargs;
    char command[100];
    char arg[400];
98 99 100
    char *argv[20];
    int i, ret;
    xmlChar *ans;
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124

    while (1) {
	cmdline = xmlShellReadline("> ");
	if (cmdline == NULL)
	    return;

	/*
	 * Parse the command itself
	 */
	cur = cmdline;
	nbargs = 0;
	while ((*cur == ' ') || (*cur == '\t')) cur++;
	i = 0;
	while ((*cur != ' ') && (*cur != '\t') &&
	       (*cur != '\n') && (*cur != '\r')) {
	    if (*cur == 0)
		break;
	    command[i++] = *cur++;
	}
	command[i] = 0;
	if (i == 0) continue;
	nbargs++;

	/*
125
	 * Parse the argument string
126
	 */
127
	memset(arg, 0, sizeof(arg));
128 129 130 131 132 133 134 135 136 137 138
	while ((*cur == ' ') || (*cur == '\t')) cur++;
	i = 0;
	while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) {
	    if (*cur == 0)
		break;
	    arg[i++] = *cur++;
	}
	arg[i] = 0;
	if (i != 0) 
	    nbargs++;

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
	/*
	 * Parse the arguments
	 */
	i = 0;
	nbargs = 0;
	cur = arg;
	memset(argv, 0, sizeof(argv));
	while (*cur != 0) {
	    while ((*cur == ' ') || (*cur == '\t')) cur++;
	    if (*cur == '\'') {
		cur++;
		argv[i] = cur;
		while ((*cur != 0) && (*cur != '\'')) cur++;
		if (*cur == '\'') {
		    *cur = 0;
		    nbargs++;
		    i++;
		    cur++;
		}
	    } else if (*cur == '"') { 
		cur++;
		argv[i] = cur;
		while ((*cur != 0) && (*cur != '"')) cur++;
		if (*cur == '"') {
		    *cur = 0;
		    nbargs++;
		    i++;
		    cur++;
		}
	    } else {
		argv[i] = cur;
		while ((*cur != 0) && (*cur != ' ') && (*cur != '\t'))
		    cur++;
		*cur = 0;
		nbargs++;
		i++;
		cur++;
	    }
	}

179 180 181 182 183 184 185 186 187 188
	/*
	 * start interpreting the command
	 */
        if (!strcmp(command, "exit"))
	    break;
        if (!strcmp(command, "quit"))
	    break;
        if (!strcmp(command, "bye"))
	    break;
	if (!strcmp(command, "public")) {
189 190
	    if (nbargs != 1) {
		printf("public requires 1 arguments\n");
191
	    } else {
192 193
		ans = xmlCatalogResolvePublic((const xmlChar *) argv[0]);
		if (ans == NULL) {
194 195
		    printf("No entry for PUBLIC %s\n", argv[0]);
		} else {
196 197
		    printf("%s\n", ans);
		    xmlFree(ans);
198
		}
199 200
	    }
	} else if (!strcmp(command, "system")) {
201 202 203
	    if (nbargs != 1) {
		printf("system requires 1 arguments\n");
	    } else {
204 205
		ans = xmlCatalogResolveSystem((const xmlChar *) argv[0]);
		if (ans == NULL) {
206 207
		    printf("No entry for SYSTEM %s\n", argv[0]);
		} else {
208 209
		    printf("%s\n", ans);
		    xmlFree(ans);
210 211 212
		}
	    }
	} else if (!strcmp(command, "add")) {
213 214 215 216 217 218 219
	    if (sgml) {
		if (nbargs != 1) {
		    printf("add requires 1 argument\n");
		} else {
		    ret = xmlCatalogAdd(BAD_CAST "sgmlcatalog", NULL,
			                BAD_CAST argv[0]);
		}
220
	    } else {
221 222 223 224 225 226 227 228 229 230 231 232
		if ((nbargs != 3) && (nbargs != 2)) {
		    printf("add requires 2 or 3 arguments\n");
		} else {
		    if (argv[2] == NULL)
			ret = xmlCatalogAdd(BAD_CAST argv[0], NULL,
					    BAD_CAST argv[1]);
		    else
			ret = xmlCatalogAdd(BAD_CAST argv[0], BAD_CAST argv[1],
					    BAD_CAST argv[2]);
		    if (ret != 0)
			printf("add command failed\n");
		}
233 234 235 236 237 238 239 240 241 242 243 244 245
	    }
	} else if (!strcmp(command, "del")) {
	    if (nbargs != 1) {
		printf("del requires 1\n");
	    } else {
		ret = xmlCatalogRemove(BAD_CAST argv[0]);
		if (ret <= 0)
		    printf("del command failed\n");

	    }
	} else if (!strcmp(command, "resolve")) {
	    if (nbargs != 2) {
		printf("resolve requires 2 arguments\n");
246
	    } else {
247 248 249 250 251 252 253 254
		ans = xmlCatalogResolve(BAD_CAST argv[0],
			                BAD_CAST argv[1]);
		if (ans == NULL) {
		    printf("Resolver failed to find an answer\n");
		} else {
		    printf("%s\n", ans);
		    xmlFree(ans);
		}
255 256
	    }
	} else if (!strcmp(command, "dump")) {
257 258 259 260 261
	    if (nbargs != 0) {
		printf("dump has no arguments\n");
	    } else {
		xmlCatalogDump(stdout);
	    }
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
	} else if (!strcmp(command, "debug")) {
	    if (nbargs != 0) {
		printf("debug has no arguments\n");
	    } else {
		verbose++;
		xmlCatalogSetDebug(verbose);
	    }
	} else if (!strcmp(command, "quiet")) {
	    if (nbargs != 0) {
		printf("quiet has no arguments\n");
	    } else {
		if (verbose > 0)
		    verbose--;
		xmlCatalogSetDebug(verbose);
	    }
277 278 279 280 281 282 283
	} else {
	    if (strcmp(command, "help")) {
		printf("Unrecognized command %s\n", command);
	    }
	    printf("Commands available:\n");
	    printf("\tpublic PublicID: make a PUBLIC identifier lookup\n");
	    printf("\tsystem SystemID: make a SYSTEM identifier lookup\n");
284 285 286
	    printf("\tresolve PublicID SystemID: do a full resolver lookup\n");
	    printf("\tadd 'type' 'orig' 'replace' : add an entry\n");
	    printf("\tdel 'values' : remove values\n");
287
	    printf("\tdump: print the current catalog state\n");
288 289
	    printf("\tdebug: increase the verbosity level\n");
	    printf("\tquiet: decrease the verbosity level\n");
290 291 292 293 294 295 296 297 298 299 300 301
	    printf("\texit:  quit the shell\n");
	} 
	free(cmdline); /* not xmlFree here ! */
    }
}

/************************************************************************
 * 									*
 * 			Main						*
 * 									*
 ************************************************************************/
static void usage(const char *name) {
302 303
    printf("Usage : %s [options] catalogfile entities...\n", name);
    printf("\tParse the catalog file and query it for the entities\n");
304
    printf("\t--sgml : handle SGML Super catalogs for --add and --del\n");
305
    printf("\t--shell : run a shell allowing interactive queries\n");
306
    printf("\t--create : create a new catalog\n");
307 308 309 310
    printf("\t--add 'type' 'orig' 'replace' : add an entry\n");
    printf("\t--del 'values' : remove values\n");
    printf("\t--noout: avoid dumping the result on stdout\n");
    printf("\t         used with add or del, it saves the catalog changes\n");
311
    printf("\t         and with --sgml it also updates the super catalog\n");
312 313 314 315
    printf("\t-v --verbose : provide debug informations\n");
}
int main(int argc, char **argv) {
    int i;
316
    int ret;
317
    int exit_value = 0;
318

319 320 321 322 323 324 325 326 327 328 329 330

    if (argc <= 1) {
	usage(argv[0]);
	return(1);
    }

    LIBXML_TEST_VERSION
    for (i = 1; i < argc ; i++) {
	if (!strcmp(argv[i], "-"))
	    break;

	if (argv[i][0] != '-')
331
	    break;
332 333 334 335 336
	if ((!strcmp(argv[i], "-verbose")) ||
	    (!strcmp(argv[i], "-v")) ||
	    (!strcmp(argv[i], "--verbose"))) {
	    verbose++;
	    xmlCatalogSetDebug(verbose);
337 338 339
	} else if ((!strcmp(argv[i], "-noout")) ||
	    (!strcmp(argv[i], "--noout"))) {
            noout = 1;
340 341 342 343
	} else if ((!strcmp(argv[i], "-shell")) ||
	    (!strcmp(argv[i], "--shell"))) {
	    shell++;
            noout = 1;
344 345 346
	} else if ((!strcmp(argv[i], "-sgml")) ||
	    (!strcmp(argv[i], "--sgml"))) {
	    sgml++;
347 348 349
	} else if ((!strcmp(argv[i], "-create")) ||
	    (!strcmp(argv[i], "--create"))) {
	    create++;
350 351 352
	} else if ((!strcmp(argv[i], "-convert")) ||
	    (!strcmp(argv[i], "--convert"))) {
	    convert++;
353 354
	} else if ((!strcmp(argv[i], "-add")) ||
	    (!strcmp(argv[i], "--add"))) {
355
	    if (sgml)
356
		i += 2;
357 358
	    else
		i += 3;
359 360 361 362 363
	    add++;
	} else if ((!strcmp(argv[i], "-del")) ||
	    (!strcmp(argv[i], "--del"))) {
	    i += 1;
	    del++;
364 365 366 367 368 369 370 371
	} else {
	    fprintf(stderr, "Unknown option %s\n", argv[i]);
	    usage(argv[0]);
	    return(1);
	}
    }

    for (i = 1; i < argc; i++) {
372 373
	if ((!strcmp(argv[i], "-add")) ||
	    (!strcmp(argv[i], "--add"))) {
374
	    if (sgml)
375
		i += 2;
376 377
	    else
		i += 3;
378 379 380 381
	    continue;
	} else if ((!strcmp(argv[i], "-del")) ||
	    (!strcmp(argv[i], "--del"))) {
	    i += 1;
382
	    continue;
383 384 385
	} else if (argv[i][0] == '-')
	    continue;
	filename = argv[i];
386
	if (!sgml) {
387
	    ret = xmlLoadCatalog(argv[i]);
388 389 390
	    if ((ret < 0) && (create)) {
		xmlCatalogAdd(BAD_CAST "catalog", BAD_CAST argv[i], NULL);
	    }
391
	}
392 393 394
	break;
    }

395 396
    if (convert)
        ret = xmlCatalogConvert();
397

398
    if ((add) || (del)) {
399 400 401 402 403 404
	for (i = 1; i < argc ; i++) {
	    if (!strcmp(argv[i], "-"))
		break;

	    if (argv[i][0] != '-')
		continue;
405 406 407 408 409 410
	    if (strcmp(argv[i], "-add") && strcmp(argv[i], "--add") &&
		strcmp(argv[i], "-del") && strcmp(argv[i], "--del"))
		continue;

	    if (sgml) {
		/*
411
		 * Maintenance of SGML catalogs.
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
		 */
		xmlCatalogPtr catal = NULL;
		xmlCatalogPtr super = NULL;

		catal = xmlLoadSGMLSuperCatalog(argv[i + 1]);

		if ((!strcmp(argv[i], "-add")) ||
		    (!strcmp(argv[i], "--add"))) {
		    if (catal == NULL)
			catal = xmlNewCatalog(1);
		    super = xmlLoadSGMLSuperCatalog(XML_SGML_DEFAULT_CATALOG);
		    if (super == NULL)
			super = xmlNewCatalog(1);

		    xmlACatalogAdd(catal, BAD_CAST "CATALOG",
					 BAD_CAST argv[i + 2], NULL);
		    xmlACatalogAdd(super, BAD_CAST "CATALOG",
					 BAD_CAST argv[i + 1], NULL);
430
		} else {
431 432
		    if (catal != NULL)
			ret = xmlACatalogRemove(catal, BAD_CAST argv[i + 2]);
433
		    else
434
			ret = -1;
435 436
		    if (ret < 0) {
			fprintf(stderr, "Failed to remove entry from %s\n",
437
				argv[i + 1]);
438 439
			exit_value = 1;
		    }
440 441 442 443 444 445 446
		    if ((noout) && (catal != NULL) &&
			(xmlCatalogIsEmpty(catal))) {
			super = xmlLoadSGMLSuperCatalog(
				   XML_SGML_DEFAULT_CATALOG);
			if (super != NULL) {
			    ret = xmlACatalogRemove(super,
				    BAD_CAST argv[i + 1]);
447
			    if (ret < 0) {
448
				fprintf(stderr,
449
					"Failed to remove entry from %s\n",
450
					XML_SGML_DEFAULT_CATALOG);
451 452
				exit_value = 1;
			    }
453 454 455 456 457 458 459
			}
		    }
		}
		if (noout) {
		    FILE *out;

		    if (xmlCatalogIsEmpty(catal)) {
460
			remove(argv[i + 1]);
461 462 463 464 465
		    } else {
			out = fopen(argv[i + 1], "w");
			if (out == NULL) {
			    fprintf(stderr, "could not open %s for saving\n",
				    argv[i + 1]);
466
			    exit_value = 2;
467 468 469 470 471 472 473 474
			    noout = 0;
			} else {
			    xmlACatalogDump(catal, out);
			    fclose(out);
			}
		    }
		    if (super != NULL) {
			if (xmlCatalogIsEmpty(super)) {
475
			    remove(XML_SGML_DEFAULT_CATALOG);
476 477 478 479 480 481
			} else {
			    out = fopen(XML_SGML_DEFAULT_CATALOG, "w");
			    if (out == NULL) {
				fprintf(stderr,
					"could not open %s for saving\n",
					XML_SGML_DEFAULT_CATALOG);
482
				exit_value = 2;
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
				noout = 0;
			    } else {
				
				xmlACatalogDump(super, out);
				fclose(out);
			    }
			}
		    }
		} else {
		    xmlACatalogDump(catal, stdout);
		}
		i += 2;
	    } else {
		if ((!strcmp(argv[i], "-add")) ||
		    (!strcmp(argv[i], "--add"))) {
			if ((argv[i + 3] == NULL) || (argv[i + 3][0] == 0))
			    ret = xmlCatalogAdd(BAD_CAST argv[i + 1], NULL,
						BAD_CAST argv[i + 2]);
			else
			    ret = xmlCatalogAdd(BAD_CAST argv[i + 1],
						BAD_CAST argv[i + 2],
						BAD_CAST argv[i + 3]);
505
			if (ret != 0) {
506
			    printf("add command failed\n");
507 508
			    exit_value = 3;
			}
509 510 511 512
			i += 3;
		} else if ((!strcmp(argv[i], "-del")) ||
		    (!strcmp(argv[i], "--del"))) {
		    ret = xmlCatalogRemove(BAD_CAST argv[i + 1]);
513 514 515 516 517
		    if (ret < 0) {
			fprintf(stderr, "Failed to remove entry %s\n",
				argv[i + 1]);
			exit_value = 1;
		    }
518
		    i += 1;
519
		}
520 521 522
	    }
	}
	
523
    } else if (shell) {
524
	usershell();
525 526 527 528 529 530 531 532 533 534
    } else {
	for (i++; i < argc; i++) {
	    xmlURIPtr uri;
	    xmlChar *ans;
	    
	    uri = xmlParseURI(argv[i]);
	    if (uri == NULL) {
		ans = xmlCatalogResolvePublic((const xmlChar *) argv[i]);
		if (ans == NULL) {
		    printf("No entry for PUBLIC %s\n", argv[i]);
535
		    exit_value = 4;
536 537 538 539 540 541 542 543 544
		} else {
		    printf("%s\n", ans);
		    xmlFree(ans);
		}
	    } else {
                xmlFreeURI(uri);
		ans = xmlCatalogResolveSystem((const xmlChar *) argv[i]);
		if (ans == NULL) {
		    printf("No entry for SYSTEM %s\n", argv[i]);
545
		    exit_value = 4;
546 547 548 549 550 551
		} else {
		    printf("%s\n", ans);
		    xmlFree(ans);
		}
	    }
	}
552
    }
553
    if ((!sgml) && ((add) || (del) || (create) || (convert))) {
554 555 556 557 558 559
	if (noout) {
	    FILE *out;

	    out = fopen(filename, "w");
	    if (out == NULL) {
		fprintf(stderr, "could not open %s for saving\n", filename);
560
		exit_value = 2;
561 562 563 564 565 566 567 568
		noout = 0;
	    } else {
		xmlCatalogDump(out);
	    }
	} else {
	    xmlCatalogDump(stdout);
	}
    }
569 570 571 572 573 574

    /*
     * Cleanup and check for memory leaks
     */
    xmlCleanupParser();
    xmlMemoryDump();
575
    return(exit_value);
576 577 578 579 580 581 582
}
#else
int main(int argc, char **argv) {
    fprintf(stderr, "libxml was not compiled with catalog support\n");
    return(1);
}
#endif