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