blob: b76719d9a59c166fc54ed42b5c08b8dafd28778b [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
18
Bjorn Reese45029602001-08-21 09:23:53 +000019#ifdef HAVE_LIBREADLINE
20#include <readline/readline.h>
21#ifdef HAVE_LIBHISTORY
22#include <readline/history.h>
23#endif
24#endif
25
Daniel Veillard344cee72001-08-20 00:08:40 +000026#include <libxml/xmlmemory.h>
27#include <libxml/uri.h>
28#include <libxml/catalog.h>
29#include <libxml/parser.h>
30
31static int shell = 0;
Daniel Veillard82d75332001-10-08 15:01:59 +000032static int sgml = 0;
Daniel Veillard344cee72001-08-20 00:08:40 +000033static int noout = 0;
Daniel Veillarde7ead2d2001-08-22 23:44:09 +000034static int create = 0;
Daniel Veillardcda96922001-08-21 10:56:31 +000035static int add = 0;
36static int del = 0;
Daniel Veillard6c5f9d12001-08-25 13:33:14 +000037static int convert = 0;
Daniel Veillard344cee72001-08-20 00:08:40 +000038static int verbose = 0;
Daniel Veillardcda96922001-08-21 10:56:31 +000039static char *filename;
Daniel Veillard344cee72001-08-20 00:08:40 +000040
41#ifdef LIBXML_CATALOG_ENABLED
42/************************************************************************
43 * *
44 * Shell Interface *
45 * *
46 ************************************************************************/
47/**
48 * xmlShellReadline:
49 * @prompt: the prompt value
50 *
51 * Read a string
52 *
53 * Returns a pointer to it or NULL on EOF the caller is expected to
54 * free the returned string.
55 */
56static char *
57xmlShellReadline(const char *prompt) {
58#ifdef HAVE_LIBREADLINE
59 char *line_read;
60
61 /* Get a line from the user. */
62 line_read = readline (prompt);
63
64 /* If the line has any text in it, save it on the history. */
65 if (line_read && *line_read)
66 add_history (line_read);
67
68 return (line_read);
69#else
70 char line_read[501];
71
72 if (prompt != NULL)
73 fprintf(stdout, "%s", prompt);
74 if (!fgets(line_read, 500, stdin))
75 return(NULL);
76 line_read[500] = 0;
77 return(strdup(line_read));
78#endif
79}
80
81
82static void usershell(void) {
83 char *cmdline = NULL, *cur;
84 int nbargs;
85 char command[100];
86 char arg[400];
Daniel Veillardcda96922001-08-21 10:56:31 +000087 char *argv[20];
88 int i, ret;
Daniel Veillardcda96922001-08-21 10:56:31 +000089 xmlChar *ans;
Daniel Veillard344cee72001-08-20 00:08:40 +000090
91 while (1) {
92 cmdline = xmlShellReadline("> ");
93 if (cmdline == NULL)
94 return;
95
96 /*
97 * Parse the command itself
98 */
99 cur = cmdline;
100 nbargs = 0;
101 while ((*cur == ' ') || (*cur == '\t')) cur++;
102 i = 0;
103 while ((*cur != ' ') && (*cur != '\t') &&
104 (*cur != '\n') && (*cur != '\r')) {
105 if (*cur == 0)
106 break;
107 command[i++] = *cur++;
108 }
109 command[i] = 0;
110 if (i == 0) continue;
111 nbargs++;
112
113 /*
Daniel Veillardcda96922001-08-21 10:56:31 +0000114 * Parse the argument string
Daniel Veillard344cee72001-08-20 00:08:40 +0000115 */
Daniel Veillardcda96922001-08-21 10:56:31 +0000116 memset(arg, 0, sizeof(arg));
Daniel Veillard344cee72001-08-20 00:08:40 +0000117 while ((*cur == ' ') || (*cur == '\t')) cur++;
118 i = 0;
119 while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) {
120 if (*cur == 0)
121 break;
122 arg[i++] = *cur++;
123 }
124 arg[i] = 0;
125 if (i != 0)
126 nbargs++;
127
128 /*
Daniel Veillardcda96922001-08-21 10:56:31 +0000129 * Parse the arguments
130 */
131 i = 0;
132 nbargs = 0;
133 cur = arg;
134 memset(argv, 0, sizeof(argv));
135 while (*cur != 0) {
136 while ((*cur == ' ') || (*cur == '\t')) cur++;
137 if (*cur == '\'') {
138 cur++;
139 argv[i] = cur;
140 while ((*cur != 0) && (*cur != '\'')) cur++;
141 if (*cur == '\'') {
142 *cur = 0;
143 nbargs++;
144 i++;
145 cur++;
146 }
147 } else if (*cur == '"') {
148 cur++;
149 argv[i] = cur;
150 while ((*cur != 0) && (*cur != '"')) cur++;
151 if (*cur == '"') {
152 *cur = 0;
153 nbargs++;
154 i++;
155 cur++;
156 }
157 } else {
158 argv[i] = cur;
159 while ((*cur != 0) && (*cur != ' ') && (*cur != '\t'))
160 cur++;
161 *cur = 0;
162 nbargs++;
163 i++;
164 cur++;
165 }
166 }
167
168 /*
Daniel Veillard344cee72001-08-20 00:08:40 +0000169 * start interpreting the command
170 */
171 if (!strcmp(command, "exit"))
172 break;
173 if (!strcmp(command, "quit"))
174 break;
175 if (!strcmp(command, "bye"))
176 break;
177 if (!strcmp(command, "public")) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000178 if (nbargs != 1) {
179 printf("public requires 1 arguments\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000180 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000181 ans = xmlCatalogResolvePublic((const xmlChar *) argv[0]);
182 if (ans == NULL) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000183 printf("No entry for PUBLIC %s\n", argv[0]);
184 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000185 printf("%s\n", ans);
186 xmlFree(ans);
Daniel Veillardcda96922001-08-21 10:56:31 +0000187 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000188 }
189 } else if (!strcmp(command, "system")) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000190 if (nbargs != 1) {
191 printf("system requires 1 arguments\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000192 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000193 ans = xmlCatalogResolveSystem((const xmlChar *) argv[0]);
194 if (ans == NULL) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000195 printf("No entry for SYSTEM %s\n", argv[0]);
196 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000197 printf("%s\n", ans);
198 xmlFree(ans);
Daniel Veillardcda96922001-08-21 10:56:31 +0000199 }
200 }
201 } else if (!strcmp(command, "add")) {
Daniel Veillard82d75332001-10-08 15:01:59 +0000202 if (sgml) {
203 if (nbargs != 1) {
204 printf("add requires 1 argument\n");
205 } else {
206 ret = xmlCatalogAdd(BAD_CAST "sgmlcatalog", NULL,
207 BAD_CAST argv[0]);
208 }
Daniel Veillardcda96922001-08-21 10:56:31 +0000209 } else {
Daniel Veillard82d75332001-10-08 15:01:59 +0000210 if ((nbargs != 3) && (nbargs != 2)) {
211 printf("add requires 2 or 3 arguments\n");
212 } else {
213 if (argv[2] == NULL)
214 ret = xmlCatalogAdd(BAD_CAST argv[0], NULL,
215 BAD_CAST argv[1]);
216 else
217 ret = xmlCatalogAdd(BAD_CAST argv[0], BAD_CAST argv[1],
218 BAD_CAST argv[2]);
219 if (ret != 0)
220 printf("add command failed\n");
221 }
Daniel Veillardcda96922001-08-21 10:56:31 +0000222 }
223 } else if (!strcmp(command, "del")) {
224 if (nbargs != 1) {
225 printf("del requires 1\n");
226 } else {
227 ret = xmlCatalogRemove(BAD_CAST argv[0]);
228 if (ret <= 0)
229 printf("del command failed\n");
230
231 }
232 } else if (!strcmp(command, "resolve")) {
233 if (nbargs != 2) {
234 printf("resolve requires 2 arguments\n");
235 } else {
236 ans = xmlCatalogResolve(BAD_CAST argv[0],
237 BAD_CAST argv[1]);
238 if (ans == NULL) {
239 printf("Resolver failed to find an answer\n");
240 } else {
241 printf("%s\n", ans);
242 xmlFree(ans);
243 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000244 }
245 } else if (!strcmp(command, "dump")) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000246 if (nbargs != 0) {
247 printf("dump has no arguments\n");
248 } else {
249 xmlCatalogDump(stdout);
250 }
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000251 } else if (!strcmp(command, "debug")) {
252 if (nbargs != 0) {
253 printf("debug has no arguments\n");
254 } else {
255 verbose++;
256 xmlCatalogSetDebug(verbose);
257 }
258 } else if (!strcmp(command, "quiet")) {
259 if (nbargs != 0) {
260 printf("quiet has no arguments\n");
261 } else {
262 if (verbose > 0)
263 verbose--;
264 xmlCatalogSetDebug(verbose);
265 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000266 } else {
267 if (strcmp(command, "help")) {
268 printf("Unrecognized command %s\n", command);
269 }
270 printf("Commands available:\n");
271 printf("\tpublic PublicID: make a PUBLIC identifier lookup\n");
272 printf("\tsystem SystemID: make a SYSTEM identifier lookup\n");
Daniel Veillardcda96922001-08-21 10:56:31 +0000273 printf("\tresolve PublicID SystemID: do a full resolver lookup\n");
274 printf("\tadd 'type' 'orig' 'replace' : add an entry\n");
275 printf("\tdel 'values' : remove values\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000276 printf("\tdump: print the current catalog state\n");
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000277 printf("\tdebug: increase the verbosity level\n");
278 printf("\tquiet: decrease the verbosity level\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000279 printf("\texit: quit the shell\n");
280 }
281 free(cmdline); /* not xmlFree here ! */
282 }
283}
284
285/************************************************************************
286 * *
287 * Main *
288 * *
289 ************************************************************************/
290static void usage(const char *name) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000291 printf("Usage : %s [options] catalogfile entities...\n", name);
292 printf("\tParse the catalog file and query it for the entities\n");
Daniel Veillard82d75332001-10-08 15:01:59 +0000293 printf("\t--sgml : handle an SGML Super catalog\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000294 printf("\t--shell : run a shell allowing interactive queries\n");
Daniel Veillarde7ead2d2001-08-22 23:44:09 +0000295 printf("\t--create : create a new catalog\n");
Daniel Veillardcda96922001-08-21 10:56:31 +0000296 printf("\t--add 'type' 'orig' 'replace' : add an entry\n");
297 printf("\t--del 'values' : remove values\n");
298 printf("\t--noout: avoid dumping the result on stdout\n");
299 printf("\t used with add or del, it saves the catalog changes\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000300 printf("\t-v --verbose : provide debug informations\n");
301}
302int main(int argc, char **argv) {
303 int i;
Daniel Veillard6c5f9d12001-08-25 13:33:14 +0000304 int ret;
305
Daniel Veillard344cee72001-08-20 00:08:40 +0000306
307 if (argc <= 1) {
308 usage(argv[0]);
309 return(1);
310 }
311
312 LIBXML_TEST_VERSION
313 for (i = 1; i < argc ; i++) {
314 if (!strcmp(argv[i], "-"))
315 break;
316
317 if (argv[i][0] != '-')
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000318 break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000319 if ((!strcmp(argv[i], "-verbose")) ||
320 (!strcmp(argv[i], "-v")) ||
321 (!strcmp(argv[i], "--verbose"))) {
322 verbose++;
323 xmlCatalogSetDebug(verbose);
Daniel Veillardcda96922001-08-21 10:56:31 +0000324 } else if ((!strcmp(argv[i], "-noout")) ||
325 (!strcmp(argv[i], "--noout"))) {
326 noout = 1;
Daniel Veillard344cee72001-08-20 00:08:40 +0000327 } else if ((!strcmp(argv[i], "-shell")) ||
328 (!strcmp(argv[i], "--shell"))) {
329 shell++;
330 noout = 1;
Daniel Veillard82d75332001-10-08 15:01:59 +0000331 } else if ((!strcmp(argv[i], "-sgml")) ||
332 (!strcmp(argv[i], "--sgml"))) {
333 sgml++;
Daniel Veillarde7ead2d2001-08-22 23:44:09 +0000334 } else if ((!strcmp(argv[i], "-create")) ||
335 (!strcmp(argv[i], "--create"))) {
336 create++;
Daniel Veillard6c5f9d12001-08-25 13:33:14 +0000337 } else if ((!strcmp(argv[i], "-convert")) ||
338 (!strcmp(argv[i], "--convert"))) {
339 convert++;
Daniel Veillardcda96922001-08-21 10:56:31 +0000340 } else if ((!strcmp(argv[i], "-add")) ||
341 (!strcmp(argv[i], "--add"))) {
Daniel Veillard82d75332001-10-08 15:01:59 +0000342 if (sgml)
343 i += 1;
344 else
345 i += 3;
Daniel Veillardcda96922001-08-21 10:56:31 +0000346 add++;
347 } else if ((!strcmp(argv[i], "-del")) ||
348 (!strcmp(argv[i], "--del"))) {
349 i += 1;
350 del++;
Daniel Veillard344cee72001-08-20 00:08:40 +0000351 } else {
352 fprintf(stderr, "Unknown option %s\n", argv[i]);
353 usage(argv[0]);
354 return(1);
355 }
356 }
357
358 for (i = 1; i < argc; i++) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000359 if ((!strcmp(argv[i], "-add")) ||
360 (!strcmp(argv[i], "--add"))) {
Daniel Veillard82d75332001-10-08 15:01:59 +0000361 if (sgml)
362 i += 1;
363 else
364 i += 3;
Daniel Veillard344cee72001-08-20 00:08:40 +0000365 continue;
Daniel Veillardcda96922001-08-21 10:56:31 +0000366 } else if ((!strcmp(argv[i], "-del")) ||
367 (!strcmp(argv[i], "--del"))) {
368 i += 1;
369 continue;
370 } else if (argv[i][0] == '-')
371 continue;
372 filename = argv[i];
Daniel Veillardb8478642001-10-12 17:29:10 +0000373 /* !!!!!!!!!!!!!!!!!! TODO !!!!
Daniel Veillard82d75332001-10-08 15:01:59 +0000374 if (sgml)
375 ret = xmlLoadSGMLSuperCatalog(argv[i]);
376 else
Daniel Veillardb8478642001-10-12 17:29:10 +0000377 !!!!!!!!! */
Daniel Veillard82d75332001-10-08 15:01:59 +0000378 ret = xmlLoadCatalog(argv[i]);
379 if ((!sgml) && (ret < 0) && (create)) {
Daniel Veillarde7ead2d2001-08-22 23:44:09 +0000380 xmlCatalogAdd(BAD_CAST "catalog", BAD_CAST argv[i], NULL);
381 }
Daniel Veillardcda96922001-08-21 10:56:31 +0000382 break;
383 }
384
Daniel Veillard6c5f9d12001-08-25 13:33:14 +0000385 if (convert)
386 ret = xmlCatalogConvert();
Daniel Veillardcda96922001-08-21 10:56:31 +0000387
Daniel Veillard6c5f9d12001-08-25 13:33:14 +0000388 if ((add) || (del)) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000389 for (i = 1; i < argc ; i++) {
390 if (!strcmp(argv[i], "-"))
391 break;
392
393 if (argv[i][0] != '-')
394 continue;
395 if ((!strcmp(argv[i], "-add")) ||
396 (!strcmp(argv[i], "--add"))) {
Daniel Veillard82d75332001-10-08 15:01:59 +0000397 if (sgml) {
398 ret = xmlCatalogAdd(BAD_CAST "sgmlcatalog", NULL,
399 BAD_CAST argv[i + 1]);
400 i += 1;
401 } else {
402 if ((argv[i + 3] == NULL) || (argv[i + 3][0] == 0))
403 ret = xmlCatalogAdd(BAD_CAST argv[i + 1], NULL,
404 BAD_CAST argv[i + 2]);
405 else
406 ret = xmlCatalogAdd(BAD_CAST argv[i + 1],
407 BAD_CAST argv[i + 2],
408 BAD_CAST argv[i + 3]);
409 if (ret != 0)
410 printf("add command failed\n");
411 i += 3;
412 }
Daniel Veillardcda96922001-08-21 10:56:31 +0000413 } else if ((!strcmp(argv[i], "-del")) ||
414 (!strcmp(argv[i], "--del"))) {
415 ret = xmlCatalogRemove(BAD_CAST argv[i + 1]);
416 i += 1;
417 }
418 }
419
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000420 } else if (shell) {
Daniel Veillard344cee72001-08-20 00:08:40 +0000421 usershell();
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000422 } else {
423 for (i++; i < argc; i++) {
424 xmlURIPtr uri;
425 xmlChar *ans;
426
427 uri = xmlParseURI(argv[i]);
428 if (uri == NULL) {
429 ans = xmlCatalogResolvePublic((const xmlChar *) argv[i]);
430 if (ans == NULL) {
431 printf("No entry for PUBLIC %s\n", argv[i]);
432 } else {
433 printf("%s\n", ans);
434 xmlFree(ans);
435 }
436 } else {
437 xmlFreeURI(uri);
438 ans = xmlCatalogResolveSystem((const xmlChar *) argv[i]);
439 if (ans == NULL) {
440 printf("No entry for SYSTEM %s\n", argv[i]);
441 } else {
442 printf("%s\n", ans);
443 xmlFree(ans);
444 }
445 }
446 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000447 }
Daniel Veillard6c5f9d12001-08-25 13:33:14 +0000448 if ((add) || (del) || (create) || (convert)) {
Daniel Veillarde7ead2d2001-08-22 23:44:09 +0000449 if (noout) {
450 FILE *out;
451
452 out = fopen(filename, "w");
453 if (out == NULL) {
454 fprintf(stderr, "could not open %s for saving\n", filename);
455 noout = 0;
456 } else {
457 xmlCatalogDump(out);
458 }
459 } else {
460 xmlCatalogDump(stdout);
461 }
462 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000463
464 /*
465 * Cleanup and check for memory leaks
466 */
Daniel Veillard344cee72001-08-20 00:08:40 +0000467 xmlCleanupParser();
468 xmlMemoryDump();
469 return(0);
470}
471#else
472int main(int argc, char **argv) {
473 fprintf(stderr, "libxml was not compiled with catalog support\n");
474 return(1);
475}
476#endif