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