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