blob: 8e4548530a847a39f3c2cbe0a356c805c47da07e [file] [log] [blame]
Daniel Veillard344cee72001-08-20 00:08:40 +00001/*
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
Daniel Veillardc0631a62001-09-20 13:56:06 +000015#ifdef HAVE_STDLIB_H
16#include <stdlib.h>
17#endif
Daniel Veillardcd21dc72001-11-04 20:03:38 +000018#ifdef HAVE_UNISTD_H
19#include <unistd.h>
20#endif
Daniel Veillardc0631a62001-09-20 13:56:06 +000021
Bjorn Reese45029602001-08-21 09:23:53 +000022#ifdef HAVE_LIBREADLINE
23#include <readline/readline.h>
24#ifdef HAVE_LIBHISTORY
25#include <readline/history.h>
26#endif
27#endif
28
Daniel Veillard344cee72001-08-20 00:08:40 +000029#include <libxml/xmlmemory.h>
30#include <libxml/uri.h>
31#include <libxml/catalog.h>
32#include <libxml/parser.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000033#include <libxml/globals.h>
Daniel Veillard344cee72001-08-20 00:08:40 +000034
35static int shell = 0;
Daniel Veillard82d75332001-10-08 15:01:59 +000036static int sgml = 0;
Daniel Veillard344cee72001-08-20 00:08:40 +000037static int noout = 0;
Daniel Veillarde7ead2d2001-08-22 23:44:09 +000038static int create = 0;
Daniel Veillardcda96922001-08-21 10:56:31 +000039static int add = 0;
40static int del = 0;
Daniel Veillard6c5f9d12001-08-25 13:33:14 +000041static int convert = 0;
Daniel Veillard344cee72001-08-20 00:08:40 +000042static int verbose = 0;
Daniel Veillardcda96922001-08-21 10:56:31 +000043static char *filename;
Daniel Veillard344cee72001-08-20 00:08:40 +000044
45#ifdef LIBXML_CATALOG_ENABLED
Daniel Veillardcd21dc72001-11-04 20:03:38 +000046
47#define XML_SGML_DEFAULT_CATALOG "/etc/sgml/catalog"
48
Daniel Veillard344cee72001-08-20 00:08:40 +000049/************************************************************************
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 */
63static char *
64xmlShellReadline(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
79 if (prompt != NULL)
80 fprintf(stdout, "%s", prompt);
81 if (!fgets(line_read, 500, stdin))
82 return(NULL);
83 line_read[500] = 0;
84 return(strdup(line_read));
85#endif
86}
87
88
89static void usershell(void) {
90 char *cmdline = NULL, *cur;
91 int nbargs;
92 char command[100];
93 char arg[400];
Daniel Veillardcda96922001-08-21 10:56:31 +000094 char *argv[20];
95 int i, ret;
Daniel Veillardcda96922001-08-21 10:56:31 +000096 xmlChar *ans;
Daniel Veillard344cee72001-08-20 00:08:40 +000097
98 while (1) {
99 cmdline = xmlShellReadline("> ");
100 if (cmdline == NULL)
101 return;
102
103 /*
104 * Parse the command itself
105 */
106 cur = cmdline;
107 nbargs = 0;
108 while ((*cur == ' ') || (*cur == '\t')) cur++;
109 i = 0;
110 while ((*cur != ' ') && (*cur != '\t') &&
111 (*cur != '\n') && (*cur != '\r')) {
112 if (*cur == 0)
113 break;
114 command[i++] = *cur++;
115 }
116 command[i] = 0;
117 if (i == 0) continue;
118 nbargs++;
119
120 /*
Daniel Veillardcda96922001-08-21 10:56:31 +0000121 * Parse the argument string
Daniel Veillard344cee72001-08-20 00:08:40 +0000122 */
Daniel Veillardcda96922001-08-21 10:56:31 +0000123 memset(arg, 0, sizeof(arg));
Daniel Veillard344cee72001-08-20 00:08:40 +0000124 while ((*cur == ' ') || (*cur == '\t')) cur++;
125 i = 0;
126 while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) {
127 if (*cur == 0)
128 break;
129 arg[i++] = *cur++;
130 }
131 arg[i] = 0;
132 if (i != 0)
133 nbargs++;
134
135 /*
Daniel Veillardcda96922001-08-21 10:56:31 +0000136 * Parse the arguments
137 */
138 i = 0;
139 nbargs = 0;
140 cur = arg;
141 memset(argv, 0, sizeof(argv));
142 while (*cur != 0) {
143 while ((*cur == ' ') || (*cur == '\t')) cur++;
144 if (*cur == '\'') {
145 cur++;
146 argv[i] = cur;
147 while ((*cur != 0) && (*cur != '\'')) cur++;
148 if (*cur == '\'') {
149 *cur = 0;
150 nbargs++;
151 i++;
152 cur++;
153 }
154 } else if (*cur == '"') {
155 cur++;
156 argv[i] = cur;
157 while ((*cur != 0) && (*cur != '"')) cur++;
158 if (*cur == '"') {
159 *cur = 0;
160 nbargs++;
161 i++;
162 cur++;
163 }
164 } else {
165 argv[i] = cur;
166 while ((*cur != 0) && (*cur != ' ') && (*cur != '\t'))
167 cur++;
168 *cur = 0;
169 nbargs++;
170 i++;
171 cur++;
172 }
173 }
174
175 /*
Daniel Veillard344cee72001-08-20 00:08:40 +0000176 * start interpreting the command
177 */
178 if (!strcmp(command, "exit"))
179 break;
180 if (!strcmp(command, "quit"))
181 break;
182 if (!strcmp(command, "bye"))
183 break;
184 if (!strcmp(command, "public")) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000185 if (nbargs != 1) {
186 printf("public requires 1 arguments\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000187 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000188 ans = xmlCatalogResolvePublic((const xmlChar *) argv[0]);
189 if (ans == NULL) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000190 printf("No entry for PUBLIC %s\n", argv[0]);
191 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000192 printf("%s\n", ans);
193 xmlFree(ans);
Daniel Veillardcda96922001-08-21 10:56:31 +0000194 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000195 }
196 } else if (!strcmp(command, "system")) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000197 if (nbargs != 1) {
198 printf("system requires 1 arguments\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000199 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000200 ans = xmlCatalogResolveSystem((const xmlChar *) argv[0]);
201 if (ans == NULL) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000202 printf("No entry for SYSTEM %s\n", argv[0]);
203 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000204 printf("%s\n", ans);
205 xmlFree(ans);
Daniel Veillardcda96922001-08-21 10:56:31 +0000206 }
207 }
208 } else if (!strcmp(command, "add")) {
Daniel Veillard82d75332001-10-08 15:01:59 +0000209 if (sgml) {
210 if (nbargs != 1) {
211 printf("add requires 1 argument\n");
212 } else {
213 ret = xmlCatalogAdd(BAD_CAST "sgmlcatalog", NULL,
214 BAD_CAST argv[0]);
215 }
Daniel Veillardcda96922001-08-21 10:56:31 +0000216 } else {
Daniel Veillard82d75332001-10-08 15:01:59 +0000217 if ((nbargs != 3) && (nbargs != 2)) {
218 printf("add requires 2 or 3 arguments\n");
219 } else {
220 if (argv[2] == NULL)
221 ret = xmlCatalogAdd(BAD_CAST argv[0], NULL,
222 BAD_CAST argv[1]);
223 else
224 ret = xmlCatalogAdd(BAD_CAST argv[0], BAD_CAST argv[1],
225 BAD_CAST argv[2]);
226 if (ret != 0)
227 printf("add command failed\n");
228 }
Daniel Veillardcda96922001-08-21 10:56:31 +0000229 }
230 } else if (!strcmp(command, "del")) {
231 if (nbargs != 1) {
232 printf("del requires 1\n");
233 } else {
234 ret = xmlCatalogRemove(BAD_CAST argv[0]);
235 if (ret <= 0)
236 printf("del command failed\n");
237
238 }
239 } else if (!strcmp(command, "resolve")) {
240 if (nbargs != 2) {
241 printf("resolve requires 2 arguments\n");
242 } else {
243 ans = xmlCatalogResolve(BAD_CAST argv[0],
244 BAD_CAST argv[1]);
245 if (ans == NULL) {
246 printf("Resolver failed to find an answer\n");
247 } else {
248 printf("%s\n", ans);
249 xmlFree(ans);
250 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000251 }
252 } else if (!strcmp(command, "dump")) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000253 if (nbargs != 0) {
254 printf("dump has no arguments\n");
255 } else {
256 xmlCatalogDump(stdout);
257 }
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000258 } else if (!strcmp(command, "debug")) {
259 if (nbargs != 0) {
260 printf("debug has no arguments\n");
261 } else {
262 verbose++;
263 xmlCatalogSetDebug(verbose);
264 }
265 } else if (!strcmp(command, "quiet")) {
266 if (nbargs != 0) {
267 printf("quiet has no arguments\n");
268 } else {
269 if (verbose > 0)
270 verbose--;
271 xmlCatalogSetDebug(verbose);
272 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000273 } else {
274 if (strcmp(command, "help")) {
275 printf("Unrecognized command %s\n", command);
276 }
277 printf("Commands available:\n");
278 printf("\tpublic PublicID: make a PUBLIC identifier lookup\n");
279 printf("\tsystem SystemID: make a SYSTEM identifier lookup\n");
Daniel Veillardcda96922001-08-21 10:56:31 +0000280 printf("\tresolve PublicID SystemID: do a full resolver lookup\n");
281 printf("\tadd 'type' 'orig' 'replace' : add an entry\n");
282 printf("\tdel 'values' : remove values\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000283 printf("\tdump: print the current catalog state\n");
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000284 printf("\tdebug: increase the verbosity level\n");
285 printf("\tquiet: decrease the verbosity level\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000286 printf("\texit: quit the shell\n");
287 }
288 free(cmdline); /* not xmlFree here ! */
289 }
290}
291
292/************************************************************************
293 * *
294 * Main *
295 * *
296 ************************************************************************/
297static void usage(const char *name) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000298 printf("Usage : %s [options] catalogfile entities...\n", name);
299 printf("\tParse the catalog file and query it for the entities\n");
Daniel Veillardcd21dc72001-11-04 20:03:38 +0000300 printf("\t--sgml : handle SGML Super catalogs for --add and --del\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000301 printf("\t--shell : run a shell allowing interactive queries\n");
Daniel Veillarde7ead2d2001-08-22 23:44:09 +0000302 printf("\t--create : create a new catalog\n");
Daniel Veillardcda96922001-08-21 10:56:31 +0000303 printf("\t--add 'type' 'orig' 'replace' : add an entry\n");
304 printf("\t--del 'values' : remove values\n");
305 printf("\t--noout: avoid dumping the result on stdout\n");
306 printf("\t used with add or del, it saves the catalog changes\n");
Daniel Veillardcd21dc72001-11-04 20:03:38 +0000307 printf("\t and with --sgml it also updates the super catalog\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000308 printf("\t-v --verbose : provide debug informations\n");
309}
310int main(int argc, char **argv) {
311 int i;
Daniel Veillard6c5f9d12001-08-25 13:33:14 +0000312 int ret;
313
Daniel Veillard344cee72001-08-20 00:08:40 +0000314
315 if (argc <= 1) {
316 usage(argv[0]);
317 return(1);
318 }
319
320 LIBXML_TEST_VERSION
321 for (i = 1; i < argc ; i++) {
322 if (!strcmp(argv[i], "-"))
323 break;
324
325 if (argv[i][0] != '-')
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000326 break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000327 if ((!strcmp(argv[i], "-verbose")) ||
328 (!strcmp(argv[i], "-v")) ||
329 (!strcmp(argv[i], "--verbose"))) {
330 verbose++;
331 xmlCatalogSetDebug(verbose);
Daniel Veillardcda96922001-08-21 10:56:31 +0000332 } else if ((!strcmp(argv[i], "-noout")) ||
333 (!strcmp(argv[i], "--noout"))) {
334 noout = 1;
Daniel Veillard344cee72001-08-20 00:08:40 +0000335 } else if ((!strcmp(argv[i], "-shell")) ||
336 (!strcmp(argv[i], "--shell"))) {
337 shell++;
338 noout = 1;
Daniel Veillard82d75332001-10-08 15:01:59 +0000339 } else if ((!strcmp(argv[i], "-sgml")) ||
340 (!strcmp(argv[i], "--sgml"))) {
341 sgml++;
Daniel Veillarde7ead2d2001-08-22 23:44:09 +0000342 } else if ((!strcmp(argv[i], "-create")) ||
343 (!strcmp(argv[i], "--create"))) {
344 create++;
Daniel Veillard6c5f9d12001-08-25 13:33:14 +0000345 } else if ((!strcmp(argv[i], "-convert")) ||
346 (!strcmp(argv[i], "--convert"))) {
347 convert++;
Daniel Veillardcda96922001-08-21 10:56:31 +0000348 } else if ((!strcmp(argv[i], "-add")) ||
349 (!strcmp(argv[i], "--add"))) {
Daniel Veillard82d75332001-10-08 15:01:59 +0000350 if (sgml)
Daniel Veillardcd21dc72001-11-04 20:03:38 +0000351 i += 2;
Daniel Veillard82d75332001-10-08 15:01:59 +0000352 else
353 i += 3;
Daniel Veillardcda96922001-08-21 10:56:31 +0000354 add++;
355 } else if ((!strcmp(argv[i], "-del")) ||
356 (!strcmp(argv[i], "--del"))) {
357 i += 1;
358 del++;
Daniel Veillard344cee72001-08-20 00:08:40 +0000359 } else {
360 fprintf(stderr, "Unknown option %s\n", argv[i]);
361 usage(argv[0]);
362 return(1);
363 }
364 }
365
366 for (i = 1; i < argc; i++) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000367 if ((!strcmp(argv[i], "-add")) ||
368 (!strcmp(argv[i], "--add"))) {
Daniel Veillard82d75332001-10-08 15:01:59 +0000369 if (sgml)
Daniel Veillardcd21dc72001-11-04 20:03:38 +0000370 i += 2;
Daniel Veillard82d75332001-10-08 15:01:59 +0000371 else
372 i += 3;
Daniel Veillard344cee72001-08-20 00:08:40 +0000373 continue;
Daniel Veillardcda96922001-08-21 10:56:31 +0000374 } else if ((!strcmp(argv[i], "-del")) ||
375 (!strcmp(argv[i], "--del"))) {
376 i += 1;
377 continue;
378 } else if (argv[i][0] == '-')
379 continue;
380 filename = argv[i];
Daniel Veillardcd21dc72001-11-04 20:03:38 +0000381 if (!sgml) {
Daniel Veillard82d75332001-10-08 15:01:59 +0000382 ret = xmlLoadCatalog(argv[i]);
Daniel Veillardcd21dc72001-11-04 20:03:38 +0000383 if ((ret < 0) && (create)) {
384 xmlCatalogAdd(BAD_CAST "catalog", BAD_CAST argv[i], NULL);
385 }
Daniel Veillarde7ead2d2001-08-22 23:44:09 +0000386 }
Daniel Veillardcda96922001-08-21 10:56:31 +0000387 break;
388 }
389
Daniel Veillard6c5f9d12001-08-25 13:33:14 +0000390 if (convert)
391 ret = xmlCatalogConvert();
Daniel Veillardcda96922001-08-21 10:56:31 +0000392
Daniel Veillard6c5f9d12001-08-25 13:33:14 +0000393 if ((add) || (del)) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000394 for (i = 1; i < argc ; i++) {
395 if (!strcmp(argv[i], "-"))
396 break;
397
398 if (argv[i][0] != '-')
399 continue;
Daniel Veillardcd21dc72001-11-04 20:03:38 +0000400 if (strcmp(argv[i], "-add") && strcmp(argv[i], "--add") &&
401 strcmp(argv[i], "-del") && strcmp(argv[i], "--del"))
402 continue;
403
404 if (sgml) {
405 /*
406 * Maintainance of SGML catalogs.
407 */
408 xmlCatalogPtr catal = NULL;
409 xmlCatalogPtr super = NULL;
410
411 catal = xmlLoadSGMLSuperCatalog(argv[i + 1]);
412
413 if ((!strcmp(argv[i], "-add")) ||
414 (!strcmp(argv[i], "--add"))) {
415 if (catal == NULL)
416 catal = xmlNewCatalog(1);
417 super = xmlLoadSGMLSuperCatalog(XML_SGML_DEFAULT_CATALOG);
418 if (super == NULL)
419 super = xmlNewCatalog(1);
420
421 xmlACatalogAdd(catal, BAD_CAST "CATALOG",
422 BAD_CAST argv[i + 2], NULL);
423 xmlACatalogAdd(super, BAD_CAST "CATALOG",
424 BAD_CAST argv[i + 1], NULL);
Daniel Veillard82d75332001-10-08 15:01:59 +0000425 } else {
Daniel Veillardcd21dc72001-11-04 20:03:38 +0000426 if (catal != NULL)
427 ret = xmlACatalogRemove(catal, BAD_CAST argv[i + 2]);
Daniel Veillard82d75332001-10-08 15:01:59 +0000428 else
Daniel Veillardcd21dc72001-11-04 20:03:38 +0000429 ret = -1;
430 if (ret < 0)
431 fprintf(stderr, "Failed to removed entry from %s\n",
432 argv[i + 1]);
433 if ((noout) && (catal != NULL) &&
434 (xmlCatalogIsEmpty(catal))) {
435 super = xmlLoadSGMLSuperCatalog(
436 XML_SGML_DEFAULT_CATALOG);
437 if (super != NULL) {
438 ret = xmlACatalogRemove(super,
439 BAD_CAST argv[i + 1]);
440 if (ret < 0)
441 fprintf(stderr,
442 "Failed to removed entry from %s\n",
443 XML_SGML_DEFAULT_CATALOG);
444 }
445 }
Daniel Veillard82d75332001-10-08 15:01:59 +0000446 }
Daniel Veillardcd21dc72001-11-04 20:03:38 +0000447 if (noout) {
448 FILE *out;
449
450 if (xmlCatalogIsEmpty(catal)) {
451 unlink(argv[i + 1]);
452 } else {
453 out = fopen(argv[i + 1], "w");
454 if (out == NULL) {
455 fprintf(stderr, "could not open %s for saving\n",
456 argv[i + 1]);
457 noout = 0;
458 } else {
459 xmlACatalogDump(catal, out);
460 fclose(out);
461 }
462 }
463 if (super != NULL) {
464 if (xmlCatalogIsEmpty(super)) {
465 unlink(XML_SGML_DEFAULT_CATALOG);
466 } else {
467 out = fopen(XML_SGML_DEFAULT_CATALOG, "w");
468 if (out == NULL) {
469 fprintf(stderr,
470 "could not open %s for saving\n",
471 XML_SGML_DEFAULT_CATALOG);
472 noout = 0;
473 } else {
474
475 xmlACatalogDump(super, out);
476 fclose(out);
477 }
478 }
479 }
480 } else {
481 xmlACatalogDump(catal, stdout);
482 }
483 i += 2;
484 } else {
485 if ((!strcmp(argv[i], "-add")) ||
486 (!strcmp(argv[i], "--add"))) {
487 if ((argv[i + 3] == NULL) || (argv[i + 3][0] == 0))
488 ret = xmlCatalogAdd(BAD_CAST argv[i + 1], NULL,
489 BAD_CAST argv[i + 2]);
490 else
491 ret = xmlCatalogAdd(BAD_CAST argv[i + 1],
492 BAD_CAST argv[i + 2],
493 BAD_CAST argv[i + 3]);
494 if (ret != 0)
495 printf("add command failed\n");
496 i += 3;
497 } else if ((!strcmp(argv[i], "-del")) ||
498 (!strcmp(argv[i], "--del"))) {
499 ret = xmlCatalogRemove(BAD_CAST argv[i + 1]);
500 i += 1;
501 }
Daniel Veillardcda96922001-08-21 10:56:31 +0000502 }
503 }
504
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000505 } else if (shell) {
Daniel Veillard344cee72001-08-20 00:08:40 +0000506 usershell();
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000507 } else {
508 for (i++; i < argc; i++) {
509 xmlURIPtr uri;
510 xmlChar *ans;
511
512 uri = xmlParseURI(argv[i]);
513 if (uri == NULL) {
514 ans = xmlCatalogResolvePublic((const xmlChar *) argv[i]);
515 if (ans == NULL) {
516 printf("No entry for PUBLIC %s\n", argv[i]);
517 } else {
518 printf("%s\n", ans);
519 xmlFree(ans);
520 }
521 } else {
522 xmlFreeURI(uri);
523 ans = xmlCatalogResolveSystem((const xmlChar *) argv[i]);
524 if (ans == NULL) {
525 printf("No entry for SYSTEM %s\n", argv[i]);
526 } else {
527 printf("%s\n", ans);
528 xmlFree(ans);
529 }
530 }
531 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000532 }
Daniel Veillardcd21dc72001-11-04 20:03:38 +0000533 if ((!sgml) && ((add) || (del) || (create) || (convert))) {
Daniel Veillarde7ead2d2001-08-22 23:44:09 +0000534 if (noout) {
535 FILE *out;
536
537 out = fopen(filename, "w");
538 if (out == NULL) {
539 fprintf(stderr, "could not open %s for saving\n", filename);
540 noout = 0;
541 } else {
542 xmlCatalogDump(out);
543 }
544 } else {
545 xmlCatalogDump(stdout);
546 }
547 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000548
549 /*
550 * Cleanup and check for memory leaks
551 */
Daniel Veillard344cee72001-08-20 00:08:40 +0000552 xmlCleanupParser();
553 xmlMemoryDump();
554 return(0);
555}
556#else
557int main(int argc, char **argv) {
558 fprintf(stderr, "libxml was not compiled with catalog support\n");
559 return(1);
560}
561#endif