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