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

#include "libxml.h"

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

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

19 20 21 22 23 24 25
#ifdef HAVE_LIBREADLINE
#include <readline/readline.h>
#ifdef HAVE_LIBHISTORY
#include <readline/history.h>
#endif
#endif

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

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

#ifdef LIBXML_CATALOG_ENABLED
43

44
#ifndef XML_SGML_DEFAULT_CATALOG
45
#define XML_SGML_DEFAULT_CATALOG "/etc/sgml/catalog"
46
#endif
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 76
/************************************************************************
 * 									*
 * 			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];
77 78
    char *ret;
    int len;
79 80 81 82 83 84

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

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

    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++;

	/*
126
	 * Parse the argument string
127
	 */
128
	memset(arg, 0, sizeof(arg));
129 130 131 132 133 134 135 136 137 138 139
	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++;

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 179
	/*
	 * 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++;
	    }
	}

180 181 182 183 184 185 186 187 188 189
	/*
	 * start interpreting the command
	 */
        if (!strcmp(command, "exit"))
	    break;
        if (!strcmp(command, "quit"))
	    break;
        if (!strcmp(command, "bye"))
	    break;
	if (!strcmp(command, "public")) {
190 191
	    if (nbargs != 1) {
		printf("public requires 1 arguments\n");
192
	    } else {
193 194
		ans = xmlCatalogResolvePublic((const xmlChar *) argv[0]);
		if (ans == NULL) {
195 196
		    printf("No entry for PUBLIC %s\n", argv[0]);
		} else {
197 198
		    printf("%s\n", ans);
		    xmlFree(ans);
199
		}
200 201
	    }
	} else if (!strcmp(command, "system")) {
202 203 204
	    if (nbargs != 1) {
		printf("system requires 1 arguments\n");
	    } else {
205 206
		ans = xmlCatalogResolveSystem((const xmlChar *) argv[0]);
		if (ans == NULL) {
207 208
		    printf("No entry for SYSTEM %s\n", argv[0]);
		} else {
209 210
		    printf("%s\n", ans);
		    xmlFree(ans);
211 212 213
		}
	    }
	} else if (!strcmp(command, "add")) {
214
	    if (sgml) {
215 216
		if ((nbargs != 3) && (nbargs != 2)) {
		    printf("add requires 2 or 3 arguments\n");
217
		} else {
218 219 220 221 222 223 224 225
		    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");
226
		}
227
	    } else {
228 229 230 231 232 233 234 235 236 237 238 239
		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");
		}
240 241 242 243 244 245 246 247 248 249 250 251 252
	    }
	} 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");
253
	    } else {
254 255 256 257 258 259 260 261
		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);
		}
262 263
	    }
	} else if (!strcmp(command, "dump")) {
264 265 266 267 268
	    if (nbargs != 0) {
		printf("dump has no arguments\n");
	    } else {
		xmlCatalogDump(stdout);
	    }
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
	} 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);
	    }
284 285 286 287 288 289 290
	} 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");
291 292 293
	    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");
294
	    printf("\tdump: print the current catalog state\n");
295 296
	    printf("\tdebug: increase the verbosity level\n");
	    printf("\tquiet: decrease the verbosity level\n");
297 298 299 300 301 302 303 304 305 306 307 308
	    printf("\texit:  quit the shell\n");
	} 
	free(cmdline); /* not xmlFree here ! */
    }
}

/************************************************************************
 * 									*
 * 			Main						*
 * 									*
 ************************************************************************/
static void usage(const char *name) {
309 310
    printf("Usage : %s [options] catalogfile entities...\n", name);
    printf("\tParse the catalog file and query it for the entities\n");
311
    printf("\t--sgml : handle SGML Super catalogs for --add and --del\n");
312
    printf("\t--shell : run a shell allowing interactive queries\n");
313
    printf("\t--create : create a new catalog\n");
314 315 316 317
    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");
318
    printf("\t         and with --sgml it also updates the super catalog\n");
319 320 321 322
    printf("\t-v --verbose : provide debug informations\n");
}
int main(int argc, char **argv) {
    int i;
323
    int ret;
324
    int exit_value = 0;
325

326 327 328 329 330 331 332 333 334 335 336 337

    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] != '-')
338
	    break;
339 340 341 342 343
	if ((!strcmp(argv[i], "-verbose")) ||
	    (!strcmp(argv[i], "-v")) ||
	    (!strcmp(argv[i], "--verbose"))) {
	    verbose++;
	    xmlCatalogSetDebug(verbose);
344 345 346
	} else if ((!strcmp(argv[i], "-noout")) ||
	    (!strcmp(argv[i], "--noout"))) {
            noout = 1;
347 348 349 350
	} else if ((!strcmp(argv[i], "-shell")) ||
	    (!strcmp(argv[i], "--shell"))) {
	    shell++;
            noout = 1;
351 352 353
	} else if ((!strcmp(argv[i], "-sgml")) ||
	    (!strcmp(argv[i], "--sgml"))) {
	    sgml++;
354 355 356
	} else if ((!strcmp(argv[i], "-create")) ||
	    (!strcmp(argv[i], "--create"))) {
	    create++;
357 358 359
	} else if ((!strcmp(argv[i], "-convert")) ||
	    (!strcmp(argv[i], "--convert"))) {
	    convert++;
360 361
	} else if ((!strcmp(argv[i], "-add")) ||
	    (!strcmp(argv[i], "--add"))) {
362
	    if (sgml)
363
		i += 2;
364 365
	    else
		i += 3;
366 367 368 369 370
	    add++;
	} else if ((!strcmp(argv[i], "-del")) ||
	    (!strcmp(argv[i], "--del"))) {
	    i += 1;
	    del++;
371 372 373 374 375 376 377 378
	} else {
	    fprintf(stderr, "Unknown option %s\n", argv[i]);
	    usage(argv[0]);
	    return(1);
	}
    }

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

400 401
    if (convert)
        ret = xmlCatalogConvert();
402

403
    if ((add) || (del)) {
404 405 406 407 408 409
	for (i = 1; i < argc ; i++) {
	    if (!strcmp(argv[i], "-"))
		break;

	    if (argv[i][0] != '-')
		continue;
410 411 412 413 414 415
	    if (strcmp(argv[i], "-add") && strcmp(argv[i], "--add") &&
		strcmp(argv[i], "-del") && strcmp(argv[i], "--del"))
		continue;

	    if (sgml) {
		/*
416
		 * Maintenance of SGML catalogs.
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
		 */
		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);
435
		} else {
436 437
		    if (catal != NULL)
			ret = xmlACatalogRemove(catal, BAD_CAST argv[i + 2]);
438
		    else
439
			ret = -1;
440 441
		    if (ret < 0) {
			fprintf(stderr, "Failed to remove entry from %s\n",
442
				argv[i + 1]);
443 444
			exit_value = 1;
		    }
445 446 447 448 449 450 451
		    if ((noout) && (catal != NULL) &&
			(xmlCatalogIsEmpty(catal))) {
			super = xmlLoadSGMLSuperCatalog(
				   XML_SGML_DEFAULT_CATALOG);
			if (super != NULL) {
			    ret = xmlACatalogRemove(super,
				    BAD_CAST argv[i + 1]);
452
			    if (ret < 0) {
453
				fprintf(stderr,
454
					"Failed to remove entry from %s\n",
455
					XML_SGML_DEFAULT_CATALOG);
456 457
				exit_value = 1;
			    }
458 459 460 461 462 463 464
			}
		    }
		}
		if (noout) {
		    FILE *out;

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

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

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