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