blob: a66888d6d0d4c3203bd5111e6a7a59a0ba6cae0b [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
Bjorn Reese45029602001-08-21 09:23:53 +000015#ifdef HAVE_LIBREADLINE
16#include <readline/readline.h>
17#ifdef HAVE_LIBHISTORY
18#include <readline/history.h>
19#endif
20#endif
21
Daniel Veillard344cee72001-08-20 00:08:40 +000022#include <libxml/xmlmemory.h>
23#include <libxml/uri.h>
24#include <libxml/catalog.h>
25#include <libxml/parser.h>
26
27static int shell = 0;
28static int noout = 0;
Daniel Veillarde7ead2d2001-08-22 23:44:09 +000029static int create = 0;
Daniel Veillardcda96922001-08-21 10:56:31 +000030static int add = 0;
31static int del = 0;
Daniel Veillard344cee72001-08-20 00:08:40 +000032static int verbose = 0;
Daniel Veillardcda96922001-08-21 10:56:31 +000033static char *filename;
Daniel Veillard344cee72001-08-20 00:08:40 +000034
35#ifdef LIBXML_CATALOG_ENABLED
36/************************************************************************
37 * *
38 * Shell Interface *
39 * *
40 ************************************************************************/
41/**
42 * xmlShellReadline:
43 * @prompt: the prompt value
44 *
45 * Read a string
46 *
47 * Returns a pointer to it or NULL on EOF the caller is expected to
48 * free the returned string.
49 */
50static char *
51xmlShellReadline(const char *prompt) {
52#ifdef HAVE_LIBREADLINE
53 char *line_read;
54
55 /* Get a line from the user. */
56 line_read = readline (prompt);
57
58 /* If the line has any text in it, save it on the history. */
59 if (line_read && *line_read)
60 add_history (line_read);
61
62 return (line_read);
63#else
64 char line_read[501];
65
66 if (prompt != NULL)
67 fprintf(stdout, "%s", prompt);
68 if (!fgets(line_read, 500, stdin))
69 return(NULL);
70 line_read[500] = 0;
71 return(strdup(line_read));
72#endif
73}
74
75
76static void usershell(void) {
77 char *cmdline = NULL, *cur;
78 int nbargs;
79 char command[100];
80 char arg[400];
Daniel Veillardcda96922001-08-21 10:56:31 +000081 char *argv[20];
82 int i, ret;
Daniel Veillardcda96922001-08-21 10:56:31 +000083 xmlChar *ans;
Daniel Veillard344cee72001-08-20 00:08:40 +000084
85 while (1) {
86 cmdline = xmlShellReadline("> ");
87 if (cmdline == NULL)
88 return;
89
90 /*
91 * Parse the command itself
92 */
93 cur = cmdline;
94 nbargs = 0;
95 while ((*cur == ' ') || (*cur == '\t')) cur++;
96 i = 0;
97 while ((*cur != ' ') && (*cur != '\t') &&
98 (*cur != '\n') && (*cur != '\r')) {
99 if (*cur == 0)
100 break;
101 command[i++] = *cur++;
102 }
103 command[i] = 0;
104 if (i == 0) continue;
105 nbargs++;
106
107 /*
Daniel Veillardcda96922001-08-21 10:56:31 +0000108 * Parse the argument string
Daniel Veillard344cee72001-08-20 00:08:40 +0000109 */
Daniel Veillardcda96922001-08-21 10:56:31 +0000110 memset(arg, 0, sizeof(arg));
Daniel Veillard344cee72001-08-20 00:08:40 +0000111 while ((*cur == ' ') || (*cur == '\t')) cur++;
112 i = 0;
113 while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) {
114 if (*cur == 0)
115 break;
116 arg[i++] = *cur++;
117 }
118 arg[i] = 0;
119 if (i != 0)
120 nbargs++;
121
122 /*
Daniel Veillardcda96922001-08-21 10:56:31 +0000123 * Parse the arguments
124 */
125 i = 0;
126 nbargs = 0;
127 cur = arg;
128 memset(argv, 0, sizeof(argv));
129 while (*cur != 0) {
130 while ((*cur == ' ') || (*cur == '\t')) cur++;
131 if (*cur == '\'') {
132 cur++;
133 argv[i] = cur;
134 while ((*cur != 0) && (*cur != '\'')) cur++;
135 if (*cur == '\'') {
136 *cur = 0;
137 nbargs++;
138 i++;
139 cur++;
140 }
141 } else if (*cur == '"') {
142 cur++;
143 argv[i] = cur;
144 while ((*cur != 0) && (*cur != '"')) cur++;
145 if (*cur == '"') {
146 *cur = 0;
147 nbargs++;
148 i++;
149 cur++;
150 }
151 } else {
152 argv[i] = cur;
153 while ((*cur != 0) && (*cur != ' ') && (*cur != '\t'))
154 cur++;
155 *cur = 0;
156 nbargs++;
157 i++;
158 cur++;
159 }
160 }
161
162 /*
Daniel Veillard344cee72001-08-20 00:08:40 +0000163 * start interpreting the command
164 */
165 if (!strcmp(command, "exit"))
166 break;
167 if (!strcmp(command, "quit"))
168 break;
169 if (!strcmp(command, "bye"))
170 break;
171 if (!strcmp(command, "public")) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000172 if (nbargs != 1) {
173 printf("public requires 1 arguments\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000174 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000175 ans = xmlCatalogResolvePublic((const xmlChar *) argv[0]);
176 if (ans == NULL) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000177 printf("No entry for PUBLIC %s\n", argv[0]);
178 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000179 printf("%s\n", ans);
180 xmlFree(ans);
Daniel Veillardcda96922001-08-21 10:56:31 +0000181 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000182 }
183 } else if (!strcmp(command, "system")) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000184 if (nbargs != 1) {
185 printf("system requires 1 arguments\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000186 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000187 ans = xmlCatalogResolveSystem((const xmlChar *) argv[0]);
188 if (ans == NULL) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000189 printf("No entry for SYSTEM %s\n", argv[0]);
190 } else {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000191 printf("%s\n", ans);
192 xmlFree(ans);
Daniel Veillardcda96922001-08-21 10:56:31 +0000193 }
194 }
195 } else if (!strcmp(command, "add")) {
196 if ((nbargs != 3) && (nbargs != 2)) {
197 printf("add requires 2 or 3 arguments\n");
198 } else {
199 if (argv[2] == NULL)
200 ret = xmlCatalogAdd(BAD_CAST argv[0], NULL,
201 BAD_CAST argv[1]);
202 else
203 ret = xmlCatalogAdd(BAD_CAST argv[0], BAD_CAST argv[1],
204 BAD_CAST argv[2]);
205 if (ret != 0)
206 printf("add command failed\n");
207 }
208 } else if (!strcmp(command, "del")) {
209 if (nbargs != 1) {
210 printf("del requires 1\n");
211 } else {
212 ret = xmlCatalogRemove(BAD_CAST argv[0]);
213 if (ret <= 0)
214 printf("del command failed\n");
215
216 }
217 } else if (!strcmp(command, "resolve")) {
218 if (nbargs != 2) {
219 printf("resolve requires 2 arguments\n");
220 } else {
221 ans = xmlCatalogResolve(BAD_CAST argv[0],
222 BAD_CAST argv[1]);
223 if (ans == NULL) {
224 printf("Resolver failed to find an answer\n");
225 } else {
226 printf("%s\n", ans);
227 xmlFree(ans);
228 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000229 }
230 } else if (!strcmp(command, "dump")) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000231 if (nbargs != 0) {
232 printf("dump has no arguments\n");
233 } else {
234 xmlCatalogDump(stdout);
235 }
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000236 } else if (!strcmp(command, "debug")) {
237 if (nbargs != 0) {
238 printf("debug has no arguments\n");
239 } else {
240 verbose++;
241 xmlCatalogSetDebug(verbose);
242 }
243 } else if (!strcmp(command, "quiet")) {
244 if (nbargs != 0) {
245 printf("quiet has no arguments\n");
246 } else {
247 if (verbose > 0)
248 verbose--;
249 xmlCatalogSetDebug(verbose);
250 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000251 } else {
252 if (strcmp(command, "help")) {
253 printf("Unrecognized command %s\n", command);
254 }
255 printf("Commands available:\n");
256 printf("\tpublic PublicID: make a PUBLIC identifier lookup\n");
257 printf("\tsystem SystemID: make a SYSTEM identifier lookup\n");
Daniel Veillardcda96922001-08-21 10:56:31 +0000258 printf("\tresolve PublicID SystemID: do a full resolver lookup\n");
259 printf("\tadd 'type' 'orig' 'replace' : add an entry\n");
260 printf("\tdel 'values' : remove values\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000261 printf("\tdump: print the current catalog state\n");
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000262 printf("\tdebug: increase the verbosity level\n");
263 printf("\tquiet: decrease the verbosity level\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000264 printf("\texit: quit the shell\n");
265 }
266 free(cmdline); /* not xmlFree here ! */
267 }
268}
269
270/************************************************************************
271 * *
272 * Main *
273 * *
274 ************************************************************************/
275static void usage(const char *name) {
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000276 printf("Usage : %s [options] catalogfile entities...\n", name);
277 printf("\tParse the catalog file and query it for the entities\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000278 printf("\t--shell : run a shell allowing interactive queries\n");
Daniel Veillarde7ead2d2001-08-22 23:44:09 +0000279 printf("\t--create : create a new catalog\n");
Daniel Veillardcda96922001-08-21 10:56:31 +0000280 printf("\t--add 'type' 'orig' 'replace' : add an entry\n");
281 printf("\t--del 'values' : remove values\n");
282 printf("\t--noout: avoid dumping the result on stdout\n");
283 printf("\t used with add or del, it saves the catalog changes\n");
Daniel Veillard344cee72001-08-20 00:08:40 +0000284 printf("\t-v --verbose : provide debug informations\n");
285}
286int main(int argc, char **argv) {
287 int i;
288
289 if (argc <= 1) {
290 usage(argv[0]);
291 return(1);
292 }
293
294 LIBXML_TEST_VERSION
295 for (i = 1; i < argc ; i++) {
296 if (!strcmp(argv[i], "-"))
297 break;
298
299 if (argv[i][0] != '-')
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000300 break;
Daniel Veillard344cee72001-08-20 00:08:40 +0000301 if ((!strcmp(argv[i], "-verbose")) ||
302 (!strcmp(argv[i], "-v")) ||
303 (!strcmp(argv[i], "--verbose"))) {
304 verbose++;
305 xmlCatalogSetDebug(verbose);
Daniel Veillardcda96922001-08-21 10:56:31 +0000306 } else if ((!strcmp(argv[i], "-noout")) ||
307 (!strcmp(argv[i], "--noout"))) {
308 noout = 1;
Daniel Veillard344cee72001-08-20 00:08:40 +0000309 } else if ((!strcmp(argv[i], "-shell")) ||
310 (!strcmp(argv[i], "--shell"))) {
311 shell++;
312 noout = 1;
Daniel Veillarde7ead2d2001-08-22 23:44:09 +0000313 } else if ((!strcmp(argv[i], "-create")) ||
314 (!strcmp(argv[i], "--create"))) {
315 create++;
Daniel Veillardcda96922001-08-21 10:56:31 +0000316 } else if ((!strcmp(argv[i], "-add")) ||
317 (!strcmp(argv[i], "--add"))) {
318 i += 3;
319 add++;
320 } else if ((!strcmp(argv[i], "-del")) ||
321 (!strcmp(argv[i], "--del"))) {
322 i += 1;
323 del++;
Daniel Veillard344cee72001-08-20 00:08:40 +0000324 } else {
325 fprintf(stderr, "Unknown option %s\n", argv[i]);
326 usage(argv[0]);
327 return(1);
328 }
329 }
330
331 for (i = 1; i < argc; i++) {
Daniel Veillardcda96922001-08-21 10:56:31 +0000332 if ((!strcmp(argv[i], "-add")) ||
333 (!strcmp(argv[i], "--add"))) {
334 i += 3;
Daniel Veillard344cee72001-08-20 00:08:40 +0000335 continue;
Daniel Veillardcda96922001-08-21 10:56:31 +0000336 } else if ((!strcmp(argv[i], "-del")) ||
337 (!strcmp(argv[i], "--del"))) {
338 i += 1;
339 continue;
340 } else if (argv[i][0] == '-')
341 continue;
342 filename = argv[i];
Daniel Veillarde7ead2d2001-08-22 23:44:09 +0000343 if (!create) {
344 xmlLoadCatalog(argv[i]);
345 } else {
346 xmlCatalogAdd(BAD_CAST "catalog", BAD_CAST argv[i], NULL);
347 }
Daniel Veillardcda96922001-08-21 10:56:31 +0000348 break;
349 }
350
351 if ((add) || (del)) {
352 int ret;
353
354 for (i = 1; i < argc ; i++) {
355 if (!strcmp(argv[i], "-"))
356 break;
357
358 if (argv[i][0] != '-')
359 continue;
360 if ((!strcmp(argv[i], "-add")) ||
361 (!strcmp(argv[i], "--add"))) {
362 if ((argv[i + 3] == NULL) || (argv[i + 3][0] == 0))
363 ret = xmlCatalogAdd(BAD_CAST argv[i + 1], NULL,
364 BAD_CAST argv[i + 2]);
365 else
366 ret = xmlCatalogAdd(BAD_CAST argv[i + 1],
367 BAD_CAST argv[i + 2],
368 BAD_CAST argv[i + 3]);
369 if (ret != 0)
370 printf("add command failed\n");
371 i += 3;
372 } else if ((!strcmp(argv[i], "-del")) ||
373 (!strcmp(argv[i], "--del"))) {
374 ret = xmlCatalogRemove(BAD_CAST argv[i + 1]);
375 i += 1;
376 }
377 }
378
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000379 } else if (shell) {
Daniel Veillard344cee72001-08-20 00:08:40 +0000380 usershell();
Daniel Veillarde2940dd2001-08-22 00:06:49 +0000381 } else {
382 for (i++; i < argc; i++) {
383 xmlURIPtr uri;
384 xmlChar *ans;
385
386 uri = xmlParseURI(argv[i]);
387 if (uri == NULL) {
388 ans = xmlCatalogResolvePublic((const xmlChar *) argv[i]);
389 if (ans == NULL) {
390 printf("No entry for PUBLIC %s\n", argv[i]);
391 } else {
392 printf("%s\n", ans);
393 xmlFree(ans);
394 }
395 } else {
396 xmlFreeURI(uri);
397 ans = xmlCatalogResolveSystem((const xmlChar *) argv[i]);
398 if (ans == NULL) {
399 printf("No entry for SYSTEM %s\n", argv[i]);
400 } else {
401 printf("%s\n", ans);
402 xmlFree(ans);
403 }
404 }
405 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000406 }
Daniel Veillarde7ead2d2001-08-22 23:44:09 +0000407 if ((add) || (del) || (create)) {
408 if (noout) {
409 FILE *out;
410
411 out = fopen(filename, "w");
412 if (out == NULL) {
413 fprintf(stderr, "could not open %s for saving\n", filename);
414 noout = 0;
415 } else {
416 xmlCatalogDump(out);
417 }
418 } else {
419 xmlCatalogDump(stdout);
420 }
421 }
Daniel Veillard344cee72001-08-20 00:08:40 +0000422
423 /*
424 * Cleanup and check for memory leaks
425 */
Daniel Veillard344cee72001-08-20 00:08:40 +0000426 xmlCleanupParser();
427 xmlMemoryDump();
428 return(0);
429}
430#else
431int main(int argc, char **argv) {
432 fprintf(stderr, "libxml was not compiled with catalog support\n");
433 return(1);
434}
435#endif