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