1 /* 2 * xmlcatalog.c : a small utility program to handle XML catalogs 3 * 4 * See Copyright for the status of this software. 5 * 6 * daniel (at) veillard.com 7 */ 8 9 #include "libxml.h" 10 11 #include <string.h> 12 #include <stdio.h> 13 #include <stdarg.h> 14 15 #ifdef HAVE_STDLIB_H 16 #include <stdlib.h> 17 #endif 18 19 #ifdef HAVE_LIBREADLINE 20 #include <readline/readline.h> 21 #ifdef HAVE_LIBHISTORY 22 #include <readline/history.h> 23 #endif 24 #endif 25 26 #include <libxml/xmlmemory.h> 27 #include <libxml/uri.h> 28 #include <libxml/catalog.h> 29 #include <libxml/parser.h> 30 #include <libxml/globals.h> 31 32 #if defined(LIBXML_CATALOG_ENABLED) && defined(LIBXML_OUTPUT_ENABLED) 33 static int shell = 0; 34 static int sgml = 0; 35 static int noout = 0; 36 static int create = 0; 37 static int add = 0; 38 static int del = 0; 39 static int convert = 0; 40 static int no_super_update = 0; 41 static int verbose = 0; 42 static char *filename = NULL; 43 44 45 #ifndef XML_SGML_DEFAULT_CATALOG 46 #define XML_SGML_DEFAULT_CATALOG "/etc/sgml/catalog" 47 #endif 48 49 /************************************************************************ 50 * * 51 * Shell Interface * 52 * * 53 ************************************************************************/ 54 /** 55 * xmlShellReadline: 56 * @prompt: the prompt value 57 * 58 * Read a string 59 * 60 * Returns a pointer to it or NULL on EOF the caller is expected to 61 * free the returned string. 62 */ 63 static char * 64 xmlShellReadline(const char *prompt) { 65 #ifdef HAVE_LIBREADLINE 66 char *line_read; 67 68 /* Get a line from the user. */ 69 line_read = readline (prompt); 70 71 /* If the line has any text in it, save it on the history. */ 72 if (line_read && *line_read) 73 add_history (line_read); 74 75 return (line_read); 76 #else 77 char line_read[501]; 78 char *ret; 79 int len; 80 81 if (prompt != NULL) 82 fprintf(stdout, "%s", prompt); 83 if (!fgets(line_read, 500, stdin)) 84 return(NULL); 85 line_read[500] = 0; 86 len = strlen(line_read); 87 ret = (char *) malloc(len + 1); 88 if (ret != NULL) { 89 memcpy (ret, line_read, len + 1); 90 } 91 return(ret); 92 #endif 93 } 94 95 static void usershell(void) { 96 char *cmdline = NULL, *cur; 97 int nbargs; 98 char command[100]; 99 char arg[400]; 100 char *argv[20]; 101 int i, ret; 102 xmlChar *ans; 103 104 while (1) { 105 cmdline = xmlShellReadline("> "); 106 if (cmdline == NULL) 107 return; 108 109 /* 110 * Parse the command itself 111 */ 112 cur = cmdline; 113 nbargs = 0; 114 while ((*cur == ' ') || (*cur == '\t')) cur++; 115 i = 0; 116 while ((*cur != ' ') && (*cur != '\t') && 117 (*cur != '\n') && (*cur != '\r')) { 118 if (*cur == 0) 119 break; 120 command[i++] = *cur++; 121 } 122 command[i] = 0; 123 if (i == 0) { 124 free(cmdline); 125 continue; 126 } 127 nbargs++; 128 129 /* 130 * Parse the argument string 131 */ 132 memset(arg, 0, sizeof(arg)); 133 while ((*cur == ' ') || (*cur == '\t')) cur++; 134 i = 0; 135 while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) { 136 if (*cur == 0) 137 break; 138 arg[i++] = *cur++; 139 } 140 arg[i] = 0; 141 if (i != 0) 142 nbargs++; 143 144 /* 145 * Parse the arguments 146 */ 147 i = 0; 148 nbargs = 0; 149 cur = arg; 150 memset(argv, 0, sizeof(argv)); 151 while (*cur != 0) { 152 while ((*cur == ' ') || (*cur == '\t')) cur++; 153 if (*cur == '\'') { 154 cur++; 155 argv[i] = cur; 156 while ((*cur != 0) && (*cur != '\'')) cur++; 157 if (*cur == '\'') { 158 *cur = 0; 159 nbargs++; 160 i++; 161 cur++; 162 } 163 } else if (*cur == '"') { 164 cur++; 165 argv[i] = cur; 166 while ((*cur != 0) && (*cur != '"')) cur++; 167 if (*cur == '"') { 168 *cur = 0; 169 nbargs++; 170 i++; 171 cur++; 172 } 173 } else { 174 argv[i] = cur; 175 while ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) 176 cur++; 177 *cur = 0; 178 nbargs++; 179 i++; 180 cur++; 181 } 182 } 183 184 /* 185 * start interpreting the command 186 */ 187 if (!strcmp(command, "exit")) 188 break; 189 if (!strcmp(command, "quit")) 190 break; 191 if (!strcmp(command, "bye")) 192 break; 193 if (!strcmp(command, "public")) { 194 if (nbargs != 1) { 195 printf("public requires 1 arguments\n"); 196 } else { 197 ans = xmlCatalogResolvePublic((const xmlChar *) argv[0]); 198 if (ans == NULL) { 199 printf("No entry for PUBLIC %s\n", argv[0]); 200 } else { 201 printf("%s\n", (char *) ans); 202 xmlFree(ans); 203 } 204 } 205 } else if (!strcmp(command, "system")) { 206 if (nbargs != 1) { 207 printf("system requires 1 arguments\n"); 208 } else { 209 ans = xmlCatalogResolveSystem((const xmlChar *) argv[0]); 210 if (ans == NULL) { 211 printf("No entry for SYSTEM %s\n", argv[0]); 212 } else { 213 printf("%s\n", (char *) ans); 214 xmlFree(ans); 215 } 216 } 217 } else if (!strcmp(command, "add")) { 218 if (sgml) { 219 if ((nbargs != 3) && (nbargs != 2)) { 220 printf("add requires 2 or 3 arguments\n"); 221 } else { 222 if (argv[2] == NULL) 223 ret = xmlCatalogAdd(BAD_CAST argv[0], NULL, 224 BAD_CAST argv[1]); 225 else 226 ret = xmlCatalogAdd(BAD_CAST argv[0], BAD_CAST argv[1], 227 BAD_CAST argv[2]); 228 if (ret != 0) 229 printf("add command failed\n"); 230 } 231 } else { 232 if ((nbargs != 3) && (nbargs != 2)) { 233 printf("add requires 2 or 3 arguments\n"); 234 } else { 235 if (argv[2] == NULL) 236 ret = xmlCatalogAdd(BAD_CAST argv[0], NULL, 237 BAD_CAST argv[1]); 238 else 239 ret = xmlCatalogAdd(BAD_CAST argv[0], BAD_CAST argv[1], 240 BAD_CAST argv[2]); 241 if (ret != 0) 242 printf("add command failed\n"); 243 } 244 } 245 } else if (!strcmp(command, "del")) { 246 if (nbargs != 1) { 247 printf("del requires 1\n"); 248 } else { 249 ret = xmlCatalogRemove(BAD_CAST argv[0]); 250 if (ret <= 0) 251 printf("del command failed\n"); 252 253 } 254 } else if (!strcmp(command, "resolve")) { 255 if (nbargs != 2) { 256 printf("resolve requires 2 arguments\n"); 257 } else { 258 ans = xmlCatalogResolve(BAD_CAST argv[0], 259 BAD_CAST argv[1]); 260 if (ans == NULL) { 261 printf("Resolver failed to find an answer\n"); 262 } else { 263 printf("%s\n", (char *) ans); 264 xmlFree(ans); 265 } 266 } 267 } else if (!strcmp(command, "dump")) { 268 if (nbargs != 0) { 269 printf("dump has no arguments\n"); 270 } else { 271 xmlCatalogDump(stdout); 272 } 273 } else if (!strcmp(command, "debug")) { 274 if (nbargs != 0) { 275 printf("debug has no arguments\n"); 276 } else { 277 verbose++; 278 xmlCatalogSetDebug(verbose); 279 } 280 } else if (!strcmp(command, "quiet")) { 281 if (nbargs != 0) { 282 printf("quiet has no arguments\n"); 283 } else { 284 if (verbose > 0) 285 verbose--; 286 xmlCatalogSetDebug(verbose); 287 } 288 } else { 289 if (strcmp(command, "help")) { 290 printf("Unrecognized command %s\n", command); 291 } 292 printf("Commands available:\n"); 293 printf("\tpublic PublicID: make a PUBLIC identifier lookup\n"); 294 printf("\tsystem SystemID: make a SYSTEM identifier lookup\n"); 295 printf("\tresolve PublicID SystemID: do a full resolver lookup\n"); 296 printf("\tadd 'type' 'orig' 'replace' : add an entry\n"); 297 printf("\tdel 'values' : remove values\n"); 298 printf("\tdump: print the current catalog state\n"); 299 printf("\tdebug: increase the verbosity level\n"); 300 printf("\tquiet: decrease the verbosity level\n"); 301 printf("\texit: quit the shell\n"); 302 } 303 free(cmdline); /* not xmlFree here ! */ 304 } 305 } 306 307 /************************************************************************ 308 * * 309 * Main * 310 * * 311 ************************************************************************/ 312 static void usage(const char *name) { 313 /* split into 2 printf's to avoid overly long string (gcc warning) */ 314 printf("\ 315 Usage : %s [options] catalogfile entities...\n\ 316 \tParse the catalog file and query it for the entities\n\ 317 \t--sgml : handle SGML Super catalogs for --add and --del\n\ 318 \t--shell : run a shell allowing interactive queries\n\ 319 \t--create : create a new catalog\n\ 320 \t--add 'type' 'orig' 'replace' : add an XML entry\n\ 321 \t--add 'entry' : add an SGML entry\n", name); 322 printf("\ 323 \t--del 'values' : remove values\n\ 324 \t--noout: avoid dumping the result on stdout\n\ 325 \t used with --add or --del, it saves the catalog changes\n\ 326 \t and with --sgml it automatically updates the super catalog\n\ 327 \t--no-super-update: do not update the SGML super catalog\n\ 328 \t-v --verbose : provide debug informations\n"); 329 } 330 int main(int argc, char **argv) { 331 int i; 332 int ret; 333 int exit_value = 0; 334 335 336 if (argc <= 1) { 337 usage(argv[0]); 338 return(1); 339 } 340 341 LIBXML_TEST_VERSION 342 for (i = 1; i < argc ; i++) { 343 if (!strcmp(argv[i], "-")) 344 break; 345 346 if (argv[i][0] != '-') 347 break; 348 if ((!strcmp(argv[i], "-verbose")) || 349 (!strcmp(argv[i], "-v")) || 350 (!strcmp(argv[i], "--verbose"))) { 351 verbose++; 352 xmlCatalogSetDebug(verbose); 353 } else if ((!strcmp(argv[i], "-noout")) || 354 (!strcmp(argv[i], "--noout"))) { 355 noout = 1; 356 } else if ((!strcmp(argv[i], "-shell")) || 357 (!strcmp(argv[i], "--shell"))) { 358 shell++; 359 noout = 1; 360 } else if ((!strcmp(argv[i], "-sgml")) || 361 (!strcmp(argv[i], "--sgml"))) { 362 sgml++; 363 } else if ((!strcmp(argv[i], "-create")) || 364 (!strcmp(argv[i], "--create"))) { 365 create++; 366 } else if ((!strcmp(argv[i], "-convert")) || 367 (!strcmp(argv[i], "--convert"))) { 368 convert++; 369 } else if ((!strcmp(argv[i], "-no-super-update")) || 370 (!strcmp(argv[i], "--no-super-update"))) { 371 no_super_update++; 372 } else if ((!strcmp(argv[i], "-add")) || 373 (!strcmp(argv[i], "--add"))) { 374 if (sgml) 375 i += 2; 376 else 377 i += 3; 378 add++; 379 } else if ((!strcmp(argv[i], "-del")) || 380 (!strcmp(argv[i], "--del"))) { 381 i += 1; 382 del++; 383 } else { 384 fprintf(stderr, "Unknown option %s\n", argv[i]); 385 usage(argv[0]); 386 return(1); 387 } 388 } 389 390 for (i = 1; i < argc; i++) { 391 if ((!strcmp(argv[i], "-add")) || 392 (!strcmp(argv[i], "--add"))) { 393 if (sgml) 394 i += 2; 395 else 396 i += 3; 397 continue; 398 } else if ((!strcmp(argv[i], "-del")) || 399 (!strcmp(argv[i], "--del"))) { 400 i += 1; 401 402 /* No catalog entry specified */ 403 if (i == argc || (sgml && i + 1 == argc)) { 404 fprintf(stderr, "No catalog entry specified to remove from\n"); 405 usage (argv[0]); 406 return(1); 407 } 408 409 continue; 410 } else if (argv[i][0] == '-') 411 continue; 412 filename = argv[i]; 413 ret = xmlLoadCatalog(argv[i]); 414 if ((ret < 0) && (create)) { 415 xmlCatalogAdd(BAD_CAST "catalog", BAD_CAST argv[i], NULL); 416 } 417 break; 418 } 419 420 if (convert) 421 ret = xmlCatalogConvert(); 422 423 if ((add) || (del)) { 424 for (i = 1; i < argc ; i++) { 425 if (!strcmp(argv[i], "-")) 426 break; 427 428 if (argv[i][0] != '-') 429 continue; 430 if (strcmp(argv[i], "-add") && strcmp(argv[i], "--add") && 431 strcmp(argv[i], "-del") && strcmp(argv[i], "--del")) 432 continue; 433 434 if (sgml) { 435 /* 436 * Maintenance of SGML catalogs. 437 */ 438 xmlCatalogPtr catal = NULL; 439 xmlCatalogPtr super = NULL; 440 441 catal = xmlLoadSGMLSuperCatalog(argv[i + 1]); 442 443 if ((!strcmp(argv[i], "-add")) || 444 (!strcmp(argv[i], "--add"))) { 445 if (catal == NULL) 446 catal = xmlNewCatalog(1); 447 xmlACatalogAdd(catal, BAD_CAST "CATALOG", 448 BAD_CAST argv[i + 2], NULL); 449 450 if (!no_super_update) { 451 super = xmlLoadSGMLSuperCatalog(XML_SGML_DEFAULT_CATALOG); 452 if (super == NULL) 453 super = xmlNewCatalog(1); 454 455 xmlACatalogAdd(super, BAD_CAST "CATALOG", 456 BAD_CAST argv[i + 1], NULL); 457 } 458 } else { 459 if (catal != NULL) 460 ret = xmlACatalogRemove(catal, BAD_CAST argv[i + 2]); 461 else 462 ret = -1; 463 if (ret < 0) { 464 fprintf(stderr, "Failed to remove entry from %s\n", 465 argv[i + 1]); 466 exit_value = 1; 467 } 468 if ((!no_super_update) && (noout) && (catal != NULL) && 469 (xmlCatalogIsEmpty(catal))) { 470 super = xmlLoadSGMLSuperCatalog( 471 XML_SGML_DEFAULT_CATALOG); 472 if (super != NULL) { 473 ret = xmlACatalogRemove(super, 474 BAD_CAST argv[i + 1]); 475 if (ret < 0) { 476 fprintf(stderr, 477 "Failed to remove entry from %s\n", 478 XML_SGML_DEFAULT_CATALOG); 479 exit_value = 1; 480 } 481 } 482 } 483 } 484 if (noout) { 485 FILE *out; 486 487 if (xmlCatalogIsEmpty(catal)) { 488 remove(argv[i + 1]); 489 } else { 490 out = fopen(argv[i + 1], "w"); 491 if (out == NULL) { 492 fprintf(stderr, "could not open %s for saving\n", 493 argv[i + 1]); 494 exit_value = 2; 495 noout = 0; 496 } else { 497 xmlACatalogDump(catal, out); 498 fclose(out); 499 } 500 } 501 if (!no_super_update && super != NULL) { 502 if (xmlCatalogIsEmpty(super)) { 503 remove(XML_SGML_DEFAULT_CATALOG); 504 } else { 505 out = fopen(XML_SGML_DEFAULT_CATALOG, "w"); 506 if (out == NULL) { 507 fprintf(stderr, 508 "could not open %s for saving\n", 509 XML_SGML_DEFAULT_CATALOG); 510 exit_value = 2; 511 noout = 0; 512 } else { 513 514 xmlACatalogDump(super, out); 515 fclose(out); 516 } 517 } 518 } 519 } else { 520 xmlACatalogDump(catal, stdout); 521 } 522 i += 2; 523 } else { 524 if ((!strcmp(argv[i], "-add")) || 525 (!strcmp(argv[i], "--add"))) { 526 if ((argv[i + 3] == NULL) || (argv[i + 3][0] == 0)) 527 ret = xmlCatalogAdd(BAD_CAST argv[i + 1], NULL, 528 BAD_CAST argv[i + 2]); 529 else 530 ret = xmlCatalogAdd(BAD_CAST argv[i + 1], 531 BAD_CAST argv[i + 2], 532 BAD_CAST argv[i + 3]); 533 if (ret != 0) { 534 printf("add command failed\n"); 535 exit_value = 3; 536 } 537 i += 3; 538 } else if ((!strcmp(argv[i], "-del")) || 539 (!strcmp(argv[i], "--del"))) { 540 ret = xmlCatalogRemove(BAD_CAST argv[i + 1]); 541 if (ret < 0) { 542 fprintf(stderr, "Failed to remove entry %s\n", 543 argv[i + 1]); 544 exit_value = 1; 545 } 546 i += 1; 547 } 548 } 549 } 550 551 } else if (shell) { 552 usershell(); 553 } else { 554 for (i++; i < argc; i++) { 555 xmlURIPtr uri; 556 xmlChar *ans; 557 558 uri = xmlParseURI(argv[i]); 559 if (uri == NULL) { 560 ans = xmlCatalogResolvePublic((const xmlChar *) argv[i]); 561 if (ans == NULL) { 562 printf("No entry for PUBLIC %s\n", argv[i]); 563 exit_value = 4; 564 } else { 565 printf("%s\n", (char *) ans); 566 xmlFree(ans); 567 } 568 } else { 569 xmlFreeURI(uri); 570 ans = xmlCatalogResolveSystem((const xmlChar *) argv[i]); 571 if (ans == NULL) { 572 printf("No entry for SYSTEM %s\n", argv[i]); 573 ans = xmlCatalogResolveURI ((const xmlChar *) argv[i]); 574 if (ans == NULL) { 575 printf ("No entry for URI %s\n", argv[i]); 576 exit_value = 4; 577 } else { 578 printf("%s\n", (char *) ans); 579 xmlFree (ans); 580 } 581 } else { 582 printf("%s\n", (char *) ans); 583 xmlFree(ans); 584 } 585 } 586 } 587 } 588 if ((!sgml) && ((add) || (del) || (create) || (convert))) { 589 if (noout && filename && *filename) { 590 FILE *out; 591 592 out = fopen(filename, "w"); 593 if (out == NULL) { 594 fprintf(stderr, "could not open %s for saving\n", filename); 595 exit_value = 2; 596 noout = 0; 597 } else { 598 xmlCatalogDump(out); 599 } 600 } else { 601 xmlCatalogDump(stdout); 602 } 603 } 604 605 /* 606 * Cleanup and check for memory leaks 607 */ 608 xmlCleanupParser(); 609 xmlMemoryDump(); 610 return(exit_value); 611 } 612 #else 613 int main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { 614 fprintf(stderr, "libxml was not compiled with catalog and output support\n"); 615 return(1); 616 } 617 #endif 618