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