7ca66e12b7062e78ca698dd798d47616ad79fad2
[asterisk/asterisk.git] / main / xmldoc.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008, Eliel C. Sardanons (LU1ALY) <eliels@gmail.com>
5  *
6  * See http://www.asterisk.org for more information about
7  * the Asterisk project. Please do not directly contact
8  * any of the maintainers of this project for assistance;
9  * the project provides a web site, mailing lists and IRC
10  * channels for your use.
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License Version 2. See the LICENSE file
14  * at the top of the source tree.
15  */
16
17 /*! \file
18  *
19  * \brief XML Documentation API
20  *
21  * \author Eliel C. Sardanons (LU1ALY) <eliels@gmail.com>
22  *
23  * libxml2 http://www.xmlsoft.org/
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include "asterisk/_private.h"
35 #include "asterisk/paths.h"
36 #include "asterisk/linkedlists.h"
37 #include "asterisk/config.h"
38 #include "asterisk/term.h"
39 #include "asterisk/astobj2.h"
40 #include "asterisk/xmldoc.h"
41 #include "asterisk/cli.h"
42
43 #ifdef AST_XML_DOCS
44
45 /*! \brief Default documentation language. */
46 static const char default_documentation_language[] = "en_US";
47
48 /*!
49  * \brief Number of columns to print when showing the XML documentation with a
50  *         'core show application/function *' CLI command. Used in text wrapping.
51  */
52 static const int xmldoc_text_columns = 74;
53
54 /*!
55  * \brief This is a value that we will use to let the wrapping mechanism move the cursor
56  *         backward and forward xmldoc_max_diff positions before cutting the middle of a
57  *         word, trying to find a space or a \n.
58  */
59 static const int xmldoc_max_diff = 5;
60
61 /*! \brief XML documentation language. */
62 static char documentation_language[6];
63
64 /*! \brief XML documentation tree */
65 struct documentation_tree {
66         char *filename;                                 /*!< XML document filename. */
67         struct ast_xml_doc *doc;                        /*!< Open document pointer. */
68         AST_RWLIST_ENTRY(documentation_tree) entry;
69 };
70
71 static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *name, int printname);
72 static int xmldoc_parse_enumlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer);
73 static int xmldoc_parse_info(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer);
74 static int xmldoc_parse_para(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer);
75 static int xmldoc_parse_specialtags(struct ast_xml_node *fixnode, const char *tabs, const char *posttabs, struct ast_str **buffer);
76
77
78 /*!
79  * \brief Container of documentation trees
80  *
81  * \note A RWLIST is a sufficient container type to use here for now.
82  *       However, some changes will need to be made to implement ref counting
83  *       if reload support is added in the future.
84  */
85 static AST_RWLIST_HEAD_STATIC(xmldoc_tree, documentation_tree);
86
87 static const struct strcolorized_tags {
88         const char *init;      /*!< Replace initial tag with this string. */
89         const char *end;       /*!< Replace end tag with this string. */
90         const int colorfg;     /*!< Foreground color. */
91         const char *inittag;   /*!< Initial tag description. */
92         const char *endtag;    /*!< Ending tag description. */
93 } colorized_tags[] = {
94         { "<",  ">",  COLOR_GREEN,  "<replaceable>", "</replaceable>" },
95         { "\'", "\'", COLOR_BLUE,   "<literal>",     "</literal>" },
96         { "*",  "*",  COLOR_RED,    "<emphasis>",    "</emphasis>" },
97         { "\"", "\"", COLOR_YELLOW, "<filename>",    "</filename>" },
98         { "\"", "\"", COLOR_CYAN,   "<directory>",   "</directory>" },
99         { "${", "}",  COLOR_GREEN,  "<variable>",    "</variable>" },
100         { "",   "",   COLOR_BLUE,   "<value>",       "</value>" },
101         { "",   "",   COLOR_BLUE,   "<enum>",        "</enum>" },
102         { "\'", "\'", COLOR_GRAY,   "<astcli>",      "</astcli>" },
103
104         /* Special tags */
105         { "", "", COLOR_YELLOW, "<note>",   "</note>" },
106         { "", "", COLOR_RED,   "<warning>", "</warning>" }
107 };
108
109 static const struct strspecial_tags {
110         const char *tagname;            /*!< Special tag name. */
111         const char *init;               /*!< Print this at the beginning. */
112         const char *end;                /*!< Print this at the end. */
113 } special_tags[] = {
114         { "note",    "<note>NOTE:</note> ",             "" },
115         { "warning", "<warning>WARNING!!!:</warning> ", "" }
116 };
117
118 /*!
119  * \internal
120  * \brief Calculate the space in bytes used by a format string
121  *        that will be passed to a sprintf function.
122  *
123  * \param postbr The format string to use to calculate the length.
124  *
125  * \retval The postbr length.
126  */
127 static int xmldoc_postbrlen(const char *postbr)
128 {
129         int postbrreallen = 0, i;
130         size_t postbrlen;
131
132         if (!postbr) {
133                 return 0;
134         }
135         postbrlen = strlen(postbr);
136         for (i = 0; i < postbrlen; i++) {
137                 if (postbr[i] == '\t') {
138                         postbrreallen += 8 - (postbrreallen % 8);
139                 } else {
140                         postbrreallen++;
141                 }
142         }
143         return postbrreallen;
144 }
145
146 /*!
147  * \internal
148  * \brief Setup postbr to be used while wrapping the text.
149  *        Add to postbr array all the spaces and tabs at the beginning of text.
150  *
151  * \param postbr output array.
152  * \param len text array length.
153  * \param text Text with format string before the actual string.
154  */
155 static void xmldoc_setpostbr(char *postbr, size_t len, const char *text)
156 {
157         int c, postbrlen = 0;
158
159         if (!text) {
160                 return;
161         }
162
163         for (c = 0; c < len; c++) {
164                 if (text[c] == '\t' || text[c] == ' ') {
165                         postbr[postbrlen++] = text[c];
166                 } else {
167                         break;
168                 }
169         }
170         postbr[postbrlen] = '\0';
171 }
172
173 /*!
174  * \internal
175  * \brief Try to find a space or a break in text starting at currentpost
176  *         and moving at most maxdiff positions.
177  *         Helper for xmldoc_string_wrap().
178  *
179  * \param text Input string where it will search.
180  * \param currentpos Current position within text.
181  * \param maxdiff Not move more than maxdiff inside text.
182  *
183  * \retval 1 if a space or break is found inside text while moving.
184  * \retval 0 if no space or break is found.
185  */
186 static int xmldoc_wait_nextspace(const char *text, int currentpos, int maxdiff)
187 {
188         int i, textlen;
189
190         if (!text) {
191                 return 0;
192         }
193
194         textlen = strlen(text);
195         for (i = currentpos; i < textlen; i++) {
196                 if (text[i] == ESC) {
197                         /* Move to the end of the escape sequence */
198                         while (i < textlen && text[i] != 'm') {
199                                 i++;
200                         }
201                 } else if (text[i] == ' ' || text[i] == '\n') {
202                         /* Found the next space or linefeed */
203                         return 1;
204                 } else if (i - currentpos > maxdiff) {
205                         /* We have looked the max distance and didn't find it */
206                         return 0;
207                 }
208         }
209
210         /* Reached the end and did not find it */
211
212         return 0;
213 }
214
215 /*!
216  * \internal
217  * \brief Helper function for xmldoc_string_wrap().
218  *    Try to found a space or a break inside text moving backward
219  *    not more than maxdiff positions.
220  *
221  * \param text The input string where to search for a space.
222  * \param currentpos The current cursor position.
223  * \param maxdiff The max number of positions to move within text.
224  *
225  * \retval 0 If no space is found (Notice that text[currentpos] is not a space or a break)
226  * \retval > 0 If a space or a break is found, and the result is the position relative to
227  *  currentpos.
228  */
229 static int xmldoc_foundspace_backward(const char *text, int currentpos, int maxdiff)
230 {
231         int i;
232
233         for (i = currentpos; i > 0; i--) {
234                 if (text[i] == ' ' || text[i] == '\n') {
235                         return (currentpos - i);
236                 } else if (text[i] == 'm' && (text[i - 1] >= '0' || text[i - 1] <= '9')) {
237                         /* give up, we found the end of a possible ESC sequence. */
238                         return 0;
239                 } else if (currentpos - i > maxdiff) {
240                         /* give up, we can't move anymore. */
241                         return 0;
242                 }
243         }
244
245         /* we found the beginning of the text */
246
247         return 0;
248 }
249
250 /*!
251  * \internal
252  * \brief Justify a text to a number of columns.
253  *
254  * \param text Input text to be justified.
255  * \param columns Number of columns to preserve in the text.
256  * \param maxdiff Try to not cut a word when goinf down.
257  *
258  * \retval NULL on error.
259  * \retval The wrapped text.
260  */
261 static char *xmldoc_string_wrap(const char *text, int columns, int maxdiff)
262 {
263         struct ast_str *tmp;
264         char *ret, postbr[160];
265         int count = 1, i, backspace, needtobreak = 0, colmax, textlen;
266
267         /* sanity check */
268         if (!text || columns <= 0 || maxdiff < 0) {
269                 ast_log(LOG_WARNING, "Passing wrong arguments while trying to wrap the text\n");
270                 return NULL;
271         }
272
273         tmp = ast_str_create(strlen(text) * 3);
274
275         if (!tmp) {
276                 return NULL;
277         }
278
279         /* Check for blanks and tabs and put them in postbr. */
280         xmldoc_setpostbr(postbr, sizeof(postbr), text);
281         colmax = columns - xmldoc_postbrlen(postbr);
282
283         textlen = strlen(text);
284         for (i = 0; i < textlen; i++) {
285                 if (needtobreak || !(count % colmax)) {
286                         if (text[i] == ' ') {
287                                 ast_str_append(&tmp, 0, "\n%s", postbr);
288                                 needtobreak = 0;
289                                 count = 1;
290                         } else if (text[i] != '\n') {
291                                 needtobreak = 1;
292                                 if (xmldoc_wait_nextspace(text, i, maxdiff)) {
293                                         /* wait for the next space */
294                                         ast_str_append(&tmp, 0, "%c", text[i]);
295                                         continue;
296                                 }
297                                 /* Try to look backwards */
298                                 backspace = xmldoc_foundspace_backward(text, i, maxdiff);
299                                 if (backspace) {
300                                         needtobreak = 1;
301                                         ast_str_truncate(tmp, -backspace);
302                                         i -= backspace + 1;
303                                         continue;
304                                 }
305                                 ast_str_append(&tmp, 0, "\n%s", postbr);
306                                 needtobreak = 0;
307                                 count = 1;
308                         }
309                         /* skip blanks after a \n */
310                         while (text[i] == ' ') {
311                                 i++;
312                         }
313                 }
314                 if (text[i] == '\n') {
315                         xmldoc_setpostbr(postbr, sizeof(postbr), &text[i] + 1);
316                         colmax = columns - xmldoc_postbrlen(postbr);
317                         needtobreak = 0;
318                         count = 1;
319                 }
320                 if (text[i] == ESC) {
321                         /* Ignore Escape sequences. */
322                         do {
323                                 ast_str_append(&tmp, 0, "%c", text[i]);
324                                 i++;
325                         } while (i < textlen && text[i] != 'm');
326                 } else {
327                         count++;
328                 }
329                 ast_str_append(&tmp, 0, "%c", text[i]);
330         }
331
332         ret = ast_strdup(ast_str_buffer(tmp));
333         ast_free(tmp);
334
335         return ret;
336 }
337
338 char *ast_xmldoc_printable(const char *bwinput, int withcolors)
339 {
340         struct ast_str *colorized;
341         char *wrapped = NULL;
342         int i, c, len, colorsection;
343         char *tmp;
344         size_t bwinputlen;
345         static const int base_fg = COLOR_CYAN;
346
347         if (!bwinput) {
348                 return NULL;
349         }
350
351         bwinputlen = strlen(bwinput);
352
353         if (!(colorized = ast_str_create(256))) {
354                 return NULL;
355         }
356
357         if (withcolors) {
358                 ast_term_color_code(&colorized, base_fg, 0);
359                 if (!colorized) {
360                         return NULL;
361                 }
362         }
363
364         for (i = 0; i < bwinputlen; i++) {
365                 colorsection = 0;
366                 /* Check if we are at the beginning of a tag to be colorized. */
367                 for (c = 0; c < ARRAY_LEN(colorized_tags); c++) {
368                         if (strncasecmp(bwinput + i, colorized_tags[c].inittag, strlen(colorized_tags[c].inittag))) {
369                                 continue;
370                         }
371
372                         if (!(tmp = strcasestr(bwinput + i + strlen(colorized_tags[c].inittag), colorized_tags[c].endtag))) {
373                                 continue;
374                         }
375
376                         len = tmp - (bwinput + i + strlen(colorized_tags[c].inittag));
377
378                         /* Setup color */
379                         if (withcolors) {
380                                 if (ast_opt_light_background) {
381                                         /* Turn off *bright* colors */
382                                         ast_term_color_code(&colorized, colorized_tags[c].colorfg & 0x7f, 0);
383                                 } else {
384                                         /* Turn on *bright* colors */
385                                         ast_term_color_code(&colorized, colorized_tags[c].colorfg | 0x80, 0);
386                                 }
387                                 if (!colorized) {
388                                         return NULL;
389                                 }
390                         }
391
392                         /* copy initial string replace */
393                         ast_str_append(&colorized, 0, "%s", colorized_tags[c].init);
394                         if (!colorized) {
395                                 return NULL;
396                         }
397                         {
398                                 char buf[len + 1];
399                                 ast_copy_string(buf, bwinput + i + strlen(colorized_tags[c].inittag), sizeof(buf));
400                                 ast_str_append(&colorized, 0, "%s", buf);
401                         }
402                         if (!colorized) {
403                                 return NULL;
404                         }
405
406                         /* copy the ending string replace */
407                         ast_str_append(&colorized, 0, "%s", colorized_tags[c].end);
408                         if (!colorized) {
409                                 return NULL;
410                         }
411
412                         /* Continue with the last color. */
413                         if (withcolors) {
414                                 ast_term_color_code(&colorized, base_fg, 0);
415                                 if (!colorized) {
416                                         return NULL;
417                                 }
418                         }
419
420                         i += len + strlen(colorized_tags[c].endtag) + strlen(colorized_tags[c].inittag) - 1;
421                         colorsection = 1;
422                         break;
423                 }
424
425                 if (!colorsection) {
426                         ast_str_append(&colorized, 0, "%c", bwinput[i]);
427                         if (!colorized) {
428                                 return NULL;
429                         }
430                 }
431         }
432
433         if (withcolors) {
434                 ast_str_append(&colorized, 0, "%s", term_end());
435                 if (!colorized) {
436                         return NULL;
437                 }
438         }
439
440         /* Wrap the text, notice that string wrap will avoid cutting an ESC sequence. */
441         wrapped = xmldoc_string_wrap(ast_str_buffer(colorized), xmldoc_text_columns, xmldoc_max_diff);
442
443         ast_free(colorized);
444
445         return wrapped;
446 }
447
448 /*!
449  * \internal
450  * \brief Cleanup spaces and tabs after a \n
451  *
452  * \param text String to be cleaned up.
453  * \param output buffer (not already allocated).
454  * \param lastspaces Remove last spaces in the string.
455  */
456 static void xmldoc_string_cleanup(const char *text, struct ast_str **output, int lastspaces)
457 {
458         int i;
459         size_t textlen;
460
461         if (!text) {
462                 *output = NULL;
463                 return;
464         }
465
466         textlen = strlen(text);
467
468         *output = ast_str_create(textlen);
469         if (!(*output)) {
470                 ast_log(LOG_ERROR, "Problem allocating output buffer\n");
471                 return;
472         }
473
474         for (i = 0; i < textlen; i++) {
475                 if (text[i] == '\n' || text[i] == '\r') {
476                         /* remove spaces/tabs/\n after a \n. */
477                         while (text[i + 1] == '\t' || text[i + 1] == '\r' || text[i + 1] == '\n') {
478                                 i++;
479                         }
480                         ast_str_append(output, 0, " ");
481                         continue;
482                 } else {
483                         ast_str_append(output, 0, "%c", text[i]);
484                 }
485         }
486
487         /* remove last spaces (we don't want always to remove the trailing spaces). */
488         if (lastspaces) {
489                 ast_str_trim_blanks(*output);
490         }
491 }
492
493 /*!
494  * \internal
495  * \brief Check if the given attribute on the given node matches the given value.
496  *
497  * \param node the node to match
498  * \param attr the name of the attribute
499  * \param value the expected value of the attribute
500  *
501  * \retval true if the given attribute contains the given value
502  * \retval false if the given attribute does not exist or does not contain the given value
503  */
504 static int xmldoc_attribute_match(struct ast_xml_node *node, const char *attr, const char *value)
505 {
506         const char *attr_value = ast_xml_get_attribute(node, attr);
507         int match = attr_value && !strcmp(attr_value, value);
508         ast_xml_free_attr(attr_value);
509         return match;
510 }
511
512 /*!
513  * \internal
514  * \brief Get the application/function node for 'name' application/function with language 'language'
515  *        and module 'module' if we don't find any, get the first application
516  *        with 'name' no matter which language or module.
517  *
518  * \param type 'application', 'function', ...
519  * \param name Application or Function name.
520  * \param module Module item is in.
521  * \param language Try to get this language (if not found try with en_US)
522  *
523  * \retval NULL on error.
524  * \retval A node of type ast_xml_node.
525  */
526 static struct ast_xml_node *xmldoc_get_node(const char *type, const char *name, const char *module, const char *language)
527 {
528         struct ast_xml_node *node = NULL;
529         struct ast_xml_node *first_match = NULL;
530         struct ast_xml_node *lang_match = NULL;
531         struct documentation_tree *doctree;
532
533         AST_RWLIST_RDLOCK(&xmldoc_tree);
534         AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
535                 /* the core xml documents have priority over thirdparty document. */
536                 node = ast_xml_get_root(doctree->doc);
537                 if (!node) {
538                         break;
539                 }
540
541                 node = ast_xml_node_get_children(node);
542                 while ((node = ast_xml_find_element(node, type, "name", name))) {
543                         if (!ast_xml_node_get_children(node)) {
544                                 /* ignore empty nodes */
545                                 node = ast_xml_node_get_next(node);
546                                 continue;
547                         }
548
549                         if (!first_match) {
550                                 first_match = node;
551                         }
552
553                         /* Check language */
554                         if (xmldoc_attribute_match(node, "language", language)) {
555                                 if (!lang_match) {
556                                         lang_match = node;
557                                 }
558
559                                 /* if module is empty we have a match */
560                                 if (ast_strlen_zero(module)) {
561                                         break;
562                                 }
563
564                                 /* Check module */
565                                 if (xmldoc_attribute_match(node, "module", module)) {
566                                         break;
567                                 }
568                         }
569
570                         node = ast_xml_node_get_next(node);
571                 }
572
573                 /* if we matched lang and module return this match */
574                 if (node) {
575                         break;
576                 }
577
578                 /* we didn't match lang and module, just return the first
579                  * result with a matching language if we have one */
580                 if (lang_match) {
581                         node = lang_match;
582                         break;
583                 }
584
585                 /* we didn't match with only the language, just return the
586                  * first match */
587                 if (first_match) {
588                         node = first_match;
589                         break;
590                 }
591         }
592         AST_RWLIST_UNLOCK(&xmldoc_tree);
593
594         return node;
595 }
596
597 /*!
598  * \internal
599  * \brief Helper function used to build the syntax, it allocates the needed buffer (or reallocates it),
600  *        and based on the reverse value it makes use of fmt to print the parameter list inside the
601  *        realloced buffer (syntax).
602  *
603  * \param reverse We are going backwards while generating the syntax?
604  * \param len Current length of 'syntax' buffer.
605  * \param syntax Output buffer for the concatenated values.
606  * \param fmt A format string that will be used in a sprintf call.
607  */
608 static void __attribute__((format(printf, 4, 5))) xmldoc_reverse_helper(int reverse, int *len, char **syntax, const char *fmt, ...)
609 {
610         int totlen, tmpfmtlen;
611         char *tmpfmt, tmp;
612         va_list ap;
613
614         va_start(ap, fmt);
615         if (ast_vasprintf(&tmpfmt, fmt, ap) < 0) {
616                 va_end(ap);
617                 return;
618         }
619         va_end(ap);
620
621         tmpfmtlen = strlen(tmpfmt);
622         totlen = *len + tmpfmtlen + 1;
623
624         *syntax = ast_realloc(*syntax, totlen);
625
626         if (!*syntax) {
627                 ast_free(tmpfmt);
628                 return;
629         }
630
631         if (reverse) {
632                 memmove(*syntax + tmpfmtlen, *syntax, *len);
633                 /* Save this char, it will be overwritten by the \0 of strcpy. */
634                 tmp = (*syntax)[0];
635                 strcpy(*syntax, tmpfmt);
636                 /* Restore the already saved char. */
637                 (*syntax)[tmpfmtlen] = tmp;
638                 (*syntax)[totlen - 1] = '\0';
639         } else {
640                 strcpy(*syntax + *len, tmpfmt);
641         }
642
643         *len = totlen - 1;
644         ast_free(tmpfmt);
645 }
646
647 /*!
648  * \internal
649  * \brief Check if the passed node has 'what' tags inside it.
650  *
651  * \param node Root node to search 'what' elements.
652  * \param what node name to search inside node.
653  *
654  * \retval 1 If a 'what' element is found inside 'node'.
655  * \retval 0 If no 'what' is found inside 'node'.
656  */
657 static int xmldoc_has_inside(struct ast_xml_node *fixnode, const char *what)
658 {
659         struct ast_xml_node *node = fixnode;
660
661         for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
662                 if (!strcasecmp(ast_xml_node_get_name(node), what)) {
663                         return 1;
664                 }
665         }
666         return 0;
667 }
668
669 /*!
670  * \internal
671  * \brief Check if the passed node has at least one node inside it.
672  *
673  * \param node Root node to search node elements.
674  *
675  * \retval 1 If a node element is found inside 'node'.
676  * \retval 0 If no node is found inside 'node'.
677  */
678 static int xmldoc_has_nodes(struct ast_xml_node *fixnode)
679 {
680         struct ast_xml_node *node = fixnode;
681
682         for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
683                 if (strcasecmp(ast_xml_node_get_name(node), "text")) {
684                         return 1;
685                 }
686         }
687         return 0;
688 }
689
690 /*!
691  * \internal
692  * \brief Check if the passed node has at least one specialtag.
693  *
694  * \param node Root node to search "specialtags" elements.
695  *
696  * \retval 1 If a "specialtag" element is found inside 'node'.
697  * \retval 0 If no "specialtag" is found inside 'node'.
698  */
699 static int xmldoc_has_specialtags(struct ast_xml_node *fixnode)
700 {
701         struct ast_xml_node *node = fixnode;
702         int i;
703
704         for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
705                 for (i = 0; i < ARRAY_LEN(special_tags); i++) {
706                         if (!strcasecmp(ast_xml_node_get_name(node), special_tags[i].tagname)) {
707                                 return 1;
708                         }
709                 }
710         }
711         return 0;
712 }
713
714 /*!
715  * \internal
716  * \brief Build the syntax for a specified starting node.
717  *
718  * \param rootnode A pointer to the ast_xml root node.
719  * \param rootname Name of the application, function, option, etc. to build the syntax.
720  * \param childname The name of each parameter node.
721  * \param printparenthesis Boolean if we must print parenthesis if not parameters are found in the rootnode.
722  * \param printrootname Boolean if we must print the rootname before the syntax and parenthesis at the begining/end.
723  *
724  * \retval NULL on error.
725  * \retval An ast_malloc'ed string with the syntax generated.
726  */
727 static char *xmldoc_get_syntax_fun(struct ast_xml_node *rootnode, const char *rootname, const char *childname, int printparenthesis, int printrootname)
728 {
729 #define GOTONEXT(__rev, __a) (__rev ? ast_xml_node_get_prev(__a) : ast_xml_node_get_next(__a))
730 #define ISLAST(__rev, __a)  (__rev == 1 ? (ast_xml_node_get_prev(__a) ? 0 : 1) : (ast_xml_node_get_next(__a) ? 0 : 1))
731 #define MP(__a) ((multiple ? __a : ""))
732         struct ast_xml_node *node = NULL, *firstparam = NULL, *lastparam = NULL;
733         const char *paramtype, *multipletype, *paramnameattr, *attrargsep, *parenthesis, *argname;
734         int reverse, required, paramcount = 0, openbrackets = 0, len = 0, hasparams=0;
735         int reqfinode = 0, reqlanode = 0, optmidnode = 0, prnparenthesis, multiple;
736         char *syntax = NULL, *argsep, *paramname;
737
738         if (ast_strlen_zero(rootname) || ast_strlen_zero(childname)) {
739                 ast_log(LOG_WARNING, "Tried to look in XML tree with faulty rootname or childname while creating a syntax.\n");
740                 return NULL;
741         }
742
743         if (!rootnode || !ast_xml_node_get_children(rootnode)) {
744                 /* If the rootnode field is not found, at least print name. */
745                 if (ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : "")) < 0) {
746                         syntax = NULL;
747                 }
748                 return syntax;
749         }
750
751         /* Get the argument separator from the root node attribute name 'argsep', if not found
752         defaults to ','. */
753         attrargsep = ast_xml_get_attribute(rootnode, "argsep");
754         if (attrargsep) {
755                 argsep = ast_strdupa(attrargsep);
756                 ast_xml_free_attr(attrargsep);
757         } else {
758                 argsep = ast_strdupa(",");
759         }
760
761         /* Get order of evaluation. */
762         for (node = ast_xml_node_get_children(rootnode); node; node = ast_xml_node_get_next(node)) {
763                 if (strcasecmp(ast_xml_node_get_name(node), childname)) {
764                         continue;
765                 }
766                 required = 0;
767                 hasparams = 1;
768                 if ((paramtype = ast_xml_get_attribute(node, "required"))) {
769                         if (ast_true(paramtype)) {
770                                 required = 1;
771                         }
772                         ast_xml_free_attr(paramtype);
773                 }
774
775                 lastparam = node;
776                 reqlanode = required;
777
778                 if (!firstparam) {
779                         /* first parameter node */
780                         firstparam = node;
781                         reqfinode = required;
782                 }
783         }
784
785         if (!hasparams) {
786                 /* This application, function, option, etc, doesn't have any params. */
787                 if (ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : "")) < 0) {
788                         syntax = NULL;
789                 }
790                 return syntax;
791         }
792
793         if (reqfinode && reqlanode) {
794                 /* check midnode */
795                 for (node = ast_xml_node_get_children(rootnode); node; node = ast_xml_node_get_next(node)) {
796                         if (strcasecmp(ast_xml_node_get_name(node), childname)) {
797                                 continue;
798                         }
799                         if (node != firstparam && node != lastparam) {
800                                 if ((paramtype = ast_xml_get_attribute(node, "required"))) {
801                                         if (!ast_true(paramtype)) {
802                                                 optmidnode = 1;
803                                                 ast_xml_free_attr(paramtype);
804                                                 break;
805                                         }
806                                         ast_xml_free_attr(paramtype);
807                                 }
808                         }
809                 }
810         }
811
812         if ((!reqfinode && reqlanode) || (reqfinode && reqlanode && optmidnode)) {
813                 reverse = 1;
814                 node = lastparam;
815         } else {
816                 reverse = 0;
817                 node = firstparam;
818         }
819
820         /* init syntax string. */
821         if (reverse) {
822                 xmldoc_reverse_helper(reverse, &len, &syntax,
823                         (printrootname ? (printrootname == 2 ? ")]" : ")"): ""));
824         } else {
825                 xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", (printrootname ? rootname : ""),
826                         (printrootname ? (printrootname == 2 ? "[(" : "(") : ""));
827         }
828
829         for (; node; node = GOTONEXT(reverse, node)) {
830                 if (strcasecmp(ast_xml_node_get_name(node), childname)) {
831                         continue;
832                 }
833
834                 /* Get the argument name, if it is not the leaf, go inside that parameter. */
835                 if (xmldoc_has_inside(node, "argument")) {
836                         parenthesis = ast_xml_get_attribute(node, "hasparams");
837                         prnparenthesis = 0;
838                         if (parenthesis) {
839                                 prnparenthesis = ast_true(parenthesis);
840                                 if (!strcasecmp(parenthesis, "optional")) {
841                                         prnparenthesis = 2;
842                                 }
843                                 ast_xml_free_attr(parenthesis);
844                         }
845                         argname = ast_xml_get_attribute(node, "name");
846                         if (argname) {
847                                 paramname = xmldoc_get_syntax_fun(node, argname, "argument", prnparenthesis, prnparenthesis);
848                                 ast_xml_free_attr(argname);
849                         } else {
850                                 /* Malformed XML, print **UNKOWN** */
851                                 paramname = ast_strdup("**unknown**");
852                         }
853                 } else {
854                         paramnameattr = ast_xml_get_attribute(node, "name");
855                         if (!paramnameattr) {
856                                 ast_log(LOG_WARNING, "Malformed XML %s: no %s name\n", rootname, childname);
857                                 if (syntax) {
858                                         /* Free already allocated syntax */
859                                         ast_free(syntax);
860                                 }
861                                 /* to give up is ok? */
862                                 if (ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : "")) < 0) {
863                                         syntax = NULL;
864                                 }
865                                 return syntax;
866                         }
867                         paramname = ast_strdup(paramnameattr);
868                         ast_xml_free_attr(paramnameattr);
869                 }
870
871                 if (!paramname) {
872                         return NULL;
873                 }
874
875                 /* Defaults to 'false'. */
876                 multiple = 0;
877                 if ((multipletype = ast_xml_get_attribute(node, "multiple"))) {
878                         if (ast_true(multipletype)) {
879                                 multiple = 1;
880                         }
881                         ast_xml_free_attr(multipletype);
882                 }
883
884                 required = 0;   /* Defaults to 'false'. */
885                 if ((paramtype = ast_xml_get_attribute(node, "required"))) {
886                         if (ast_true(paramtype)) {
887                                 required = 1;
888                         }
889                         ast_xml_free_attr(paramtype);
890                 }
891
892                 /* build syntax core. */
893
894                 if (required) {
895                         /* First parameter */
896                         if (!paramcount) {
897                                 xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s%s", paramname, MP("["), MP(argsep), MP("...]"));
898                         } else {
899                                 /* Time to close open brackets. */
900                                 while (openbrackets > 0) {
901                                         xmldoc_reverse_helper(reverse, &len, &syntax, (reverse ? "[" : "]"));
902                                         openbrackets--;
903                                 }
904                                 if (reverse) {
905                                         xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", paramname, argsep);
906                                 } else {
907                                         xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", argsep, paramname);
908                                 }
909                                 xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s", MP("["), MP(argsep), MP("...]"));
910                         }
911                 } else {
912                         /* First parameter */
913                         if (!paramcount) {
914                                 xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s]", paramname, MP("["), MP(argsep), MP("...]"));
915                         } else {
916                                 if (ISLAST(reverse, node)) {
917                                         /* This is the last parameter. */
918                                         if (reverse) {
919                                                 xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s]%s", paramname,
920                                                                         MP("["), MP(argsep), MP("...]"), argsep);
921                                         } else {
922                                                 xmldoc_reverse_helper(reverse, &len, &syntax, "%s[%s%s%s%s]", argsep, paramname,
923                                                                         MP("["), MP(argsep), MP("...]"));
924                                         }
925                                 } else {
926                                         if (reverse) {
927                                                 xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s%s%s]", paramname, argsep,
928                                                                         MP("["), MP(argsep), MP("...]"));
929                                         } else {
930                                                 xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s%s", argsep, paramname,
931                                                                         MP("["), MP(argsep), MP("...]"));
932                                         }
933                                         openbrackets++;
934                                 }
935                         }
936                 }
937                 ast_free(paramname);
938
939                 paramcount++;
940         }
941
942         /* Time to close open brackets. */
943         while (openbrackets > 0) {
944                 xmldoc_reverse_helper(reverse, &len, &syntax, (reverse ? "[" : "]"));
945                 openbrackets--;
946         }
947
948         /* close syntax string. */
949         if (reverse) {
950                 xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", (printrootname ? rootname : ""),
951                         (printrootname ? (printrootname == 2 ? "[(" : "(") : ""));
952         } else {
953                 xmldoc_reverse_helper(reverse, &len, &syntax, (printrootname ? (printrootname == 2 ? ")]" : ")") : ""));
954         }
955
956         return syntax;
957 #undef ISLAST
958 #undef GOTONEXT
959 #undef MP
960 }
961
962 /*!
963  * \internal
964  * \brief Parse an enumlist inside a <parameter> to generate a COMMAND syntax.
965  *
966  * \param fixnode A pointer to the <enumlist> node.
967  *
968  * \retval {<unknown>} on error.
969  * \retval A string inside brackets {} with the enum's separated by pipes |.
970  */
971 static char *xmldoc_parse_cmd_enumlist(struct ast_xml_node *fixnode)
972 {
973         struct ast_xml_node *node = fixnode;
974         struct ast_str *paramname;
975         char *enumname, *ret;
976         int first = 1;
977
978         paramname = ast_str_create(128);
979         if (!paramname) {
980                 return ast_strdup("{<unkown>}");
981         }
982
983         ast_str_append(&paramname, 0, "{");
984
985         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
986                 if (strcasecmp(ast_xml_node_get_name(node), "enum")) {
987                         continue;
988                 }
989
990                 enumname = xmldoc_get_syntax_cmd(node, "", 0);
991                 if (!enumname) {
992                         continue;
993                 }
994                 if (!first) {
995                         ast_str_append(&paramname, 0, "|");
996                 }
997                 ast_str_append(&paramname, 0, "%s", enumname);
998                 first = 0;
999                 ast_free(enumname);
1000         }
1001
1002         ast_str_append(&paramname, 0, "}");
1003
1004         ret = ast_strdup(ast_str_buffer(paramname));
1005         ast_free(paramname);
1006
1007         return ret;
1008 }
1009
1010 /*!
1011  * \internal
1012  * \brief Generate a syntax of COMMAND type.
1013  *
1014  * \param fixnode The <syntax> node pointer.
1015  * \param name The name of the 'command'.
1016  * \param printname Print the name of the command before the paramters?
1017  *
1018  * \retval On error, return just 'name'.
1019  * \retval On success return the generated syntax.
1020  */
1021 static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *name, int printname)
1022 {
1023         struct ast_str *syntax;
1024         struct ast_xml_node *tmpnode, *node = fixnode;
1025         char *ret, *paramname;
1026         const char *paramtype, *attrname, *literal;
1027         int required, isenum, first = 1, isliteral;
1028
1029         if (!fixnode) {
1030                 return NULL;
1031         }
1032
1033         syntax = ast_str_create(128);
1034         if (!syntax) {
1035                 /* at least try to return something... */
1036                 return ast_strdup(name);
1037         }
1038
1039         /* append name to output string. */
1040         if (printname) {
1041                 ast_str_append(&syntax, 0, "%s", name);
1042                 first = 0;
1043         }
1044
1045         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1046                 if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
1047                         continue;
1048                 }
1049
1050                 if (xmldoc_has_inside(node, "parameter")) {
1051                         /* is this a recursive parameter. */
1052                         paramname = xmldoc_get_syntax_cmd(node, "", 0);
1053                         isenum = 1;
1054                 } else {
1055                         for (tmpnode = ast_xml_node_get_children(node); tmpnode; tmpnode = ast_xml_node_get_next(tmpnode)) {
1056                                 if (!strcasecmp(ast_xml_node_get_name(tmpnode), "enumlist")) {
1057                                         break;
1058                                 }
1059                         }
1060                         if (tmpnode) {
1061                                 /* parse enumlist (note that this is a special enumlist
1062                                 that is used to describe a syntax like {<param1>|<param2>|...} */
1063                                 paramname = xmldoc_parse_cmd_enumlist(tmpnode);
1064                                 isenum = 1;
1065                         } else {
1066                                 /* this is a simple parameter. */
1067                                 attrname = ast_xml_get_attribute(node, "name");
1068                                 if (!attrname) {
1069                                         /* ignore this bogus parameter and continue. */
1070                                         continue;
1071                                 }
1072                                 paramname = ast_strdup(attrname);
1073                                 ast_xml_free_attr(attrname);
1074                                 isenum = 0;
1075                         }
1076                 }
1077
1078                 /* Is this parameter required? */
1079                 required = 0;
1080                 paramtype = ast_xml_get_attribute(node, "required");
1081                 if (paramtype) {
1082                         required = ast_true(paramtype);
1083                         ast_xml_free_attr(paramtype);
1084                 }
1085
1086                 /* Is this a replaceable value or a fixed parameter value? */
1087                 isliteral = 0;
1088                 literal = ast_xml_get_attribute(node, "literal");
1089                 if (literal) {
1090                         isliteral = ast_true(literal);
1091                         ast_xml_free_attr(literal);
1092                 }
1093
1094                 /* if required="false" print with [...].
1095                  * if literal="true" or is enum print without <..>.
1096                  * if not first print a space at the beginning.
1097                  */
1098                 ast_str_append(&syntax, 0, "%s%s%s%s%s%s",
1099                                 (first ? "" : " "),
1100                                 (required ? "" : "["),
1101                                 (isenum || isliteral ? "" : "<"),
1102                                 paramname,
1103                                 (isenum || isliteral ? "" : ">"),
1104                                 (required ? "" : "]"));
1105                 first = 0;
1106                 ast_free(paramname);
1107         }
1108
1109         /* return a common string. */
1110         ret = ast_strdup(ast_str_buffer(syntax));
1111         ast_free(syntax);
1112
1113         return ret;
1114 }
1115
1116 /*!
1117  * \internal
1118  * \brief Generate an AMI action/event syntax.
1119  *
1120  * \param fixnode The manager action/event node pointer.
1121  * \param name The name of the manager action/event.
1122  * \param manager_type "Action" or "Event"
1123  *
1124  * \retval The generated syntax.
1125  * \retval NULL on error.
1126  */
1127 static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char *name, const char *manager_type)
1128 {
1129         struct ast_str *syntax;
1130         struct ast_xml_node *node = fixnode;
1131         const char *paramtype, *attrname;
1132         int required;
1133         char *ret;
1134
1135         if (!fixnode) {
1136                 return NULL;
1137         }
1138
1139         syntax = ast_str_create(128);
1140         if (!syntax) {
1141                 return ast_strdup(name);
1142         }
1143
1144         ast_str_append(&syntax, 0, "%s: %s", manager_type, name);
1145
1146         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1147                 if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
1148                         continue;
1149                 }
1150
1151                 /* Is this parameter required? */
1152                 required = !strcasecmp(manager_type, "event") ? 1 : 0;
1153                 paramtype = ast_xml_get_attribute(node, "required");
1154                 if (paramtype) {
1155                         required = ast_true(paramtype);
1156                         ast_xml_free_attr(paramtype);
1157                 }
1158
1159                 attrname = ast_xml_get_attribute(node, "name");
1160                 if (!attrname) {
1161                         /* ignore this bogus parameter and continue. */
1162                         continue;
1163                 }
1164
1165                 ast_str_append(&syntax, 0, "\n%s%s:%s <value>",
1166                         (required ? "" : "["),
1167                         attrname,
1168                         (required ? "" : "]"));
1169                 ast_xml_free_attr(attrname);
1170         }
1171
1172         /* return a common string. */
1173         ret = ast_strdup(ast_str_buffer(syntax));
1174         ast_free(syntax);
1175
1176         return ret;
1177 }
1178
1179 static char *xmldoc_get_syntax_config_object(struct ast_xml_node *fixnode, const char *name)
1180 {
1181         struct ast_xml_node *matchinfo, *tmp;
1182         int match;
1183         const char *attr_value;
1184         const char *text;
1185         RAII_VAR(struct ast_str *, syntax, ast_str_create(128), ast_free);
1186
1187         if (!syntax || !fixnode) {
1188                 return NULL;
1189         }
1190         if (!(matchinfo = ast_xml_find_element(ast_xml_node_get_children(fixnode), "matchInfo", NULL, NULL))) {
1191                 return NULL;
1192         }
1193         if (!(tmp  = ast_xml_find_element(ast_xml_node_get_children(matchinfo), "category", NULL, NULL))) {
1194                 return NULL;
1195         }
1196         attr_value = ast_xml_get_attribute(tmp, "match");
1197         if (attr_value) {
1198                 match = ast_true(attr_value);
1199                 text = ast_xml_get_text(tmp);
1200                 ast_str_set(&syntax, 0, "category %s /%s/", match ? "=~" : "!~", text);
1201                 ast_xml_free_attr(attr_value);
1202                 ast_xml_free_text(text);
1203         }
1204
1205         if ((tmp = ast_xml_find_element(ast_xml_node_get_children(matchinfo), "field", NULL, NULL))) {
1206                 text = ast_xml_get_text(tmp);
1207                 attr_value = ast_xml_get_attribute(tmp, "name");
1208                 ast_str_append(&syntax, 0, " matchfield: %s = %s", S_OR(attr_value, "Unknown"), text);
1209                 ast_xml_free_attr(attr_value);
1210                 ast_xml_free_text(text);
1211         }
1212         return ast_strdup(ast_str_buffer(syntax));
1213 }
1214
1215 static char *xmldoc_get_syntax_config_option(struct ast_xml_node *fixnode, const char *name)
1216 {
1217         const char *type;
1218         const char *default_value;
1219         const char *regex;
1220         RAII_VAR(struct ast_str *, syntax, ast_str_create(128), ast_free);
1221
1222         if (!syntax || !fixnode) {
1223                 return NULL;
1224         }
1225         type = ast_xml_get_attribute(fixnode, "type");
1226         default_value = ast_xml_get_attribute(fixnode, "default");
1227
1228         regex = ast_xml_get_attribute(fixnode, "regex");
1229         ast_str_set(&syntax, 0, "%s = [%s] (Default: %s) (Regex: %s)\n",
1230                 name,
1231                 type,
1232                 default_value ?: "n/a",
1233                 regex ?: "False");
1234
1235         ast_xml_free_attr(type);
1236         ast_xml_free_attr(default_value);
1237         ast_xml_free_attr(regex);
1238
1239         return ast_strdup(ast_str_buffer(syntax));
1240 }
1241
1242 /*! \brief Types of syntax that we are able to generate. */
1243 enum syntaxtype {
1244         FUNCTION_SYNTAX,
1245         MANAGER_SYNTAX,
1246         MANAGER_EVENT_SYNTAX,
1247         CONFIG_INFO_SYNTAX,
1248         CONFIG_FILE_SYNTAX,
1249         CONFIG_OPTION_SYNTAX,
1250         CONFIG_OBJECT_SYNTAX,
1251         COMMAND_SYNTAX
1252 };
1253
1254 /*! \brief Mapping between type of node and type of syntax to generate. */
1255 static struct strsyntaxtype {
1256         const char *type;
1257         enum syntaxtype stxtype;
1258 } stxtype[] = {
1259     { "function",     FUNCTION_SYNTAX      },
1260     { "application",  FUNCTION_SYNTAX      },
1261     { "manager",      MANAGER_SYNTAX       },
1262     { "managerEvent", MANAGER_EVENT_SYNTAX },
1263     { "configInfo",   CONFIG_INFO_SYNTAX   },
1264     { "configFile",   CONFIG_FILE_SYNTAX   },
1265     { "configOption", CONFIG_OPTION_SYNTAX },
1266     { "configObject", CONFIG_OBJECT_SYNTAX },
1267     { "agi",          COMMAND_SYNTAX       },
1268 };
1269
1270 /*!
1271  * \internal
1272  * \brief Get syntax type based on type of node.
1273  *
1274  * \param type Type of node.
1275  *
1276  * \retval The type of syntax to generate based on the type of node.
1277  */
1278 static enum syntaxtype xmldoc_get_syntax_type(const char *type)
1279 {
1280         int i;
1281         for (i=0; i < ARRAY_LEN(stxtype); i++) {
1282                 if (!strcasecmp(stxtype[i].type, type)) {
1283                         return stxtype[i].stxtype;
1284                 }
1285         }
1286
1287         return FUNCTION_SYNTAX;
1288 }
1289
1290 /*!
1291  * \internal
1292  * \brief Build syntax information for an item
1293  * \param node  The syntax node to parse
1294  * \param type  The source type
1295  * \param name  The name of the item that the syntax describes
1296  *
1297  * \note This method exists for when you already have the node.  This
1298  * prevents having to lock the documentation tree twice
1299  *
1300  * \retval A malloc'd character pointer to the syntax of the item
1301  * \retval NULL on failure
1302  *
1303  * \since 11
1304  */
1305 static char *_ast_xmldoc_build_syntax(struct ast_xml_node *root_node, const char *type, const char *name)
1306 {
1307         char *syntax = NULL;
1308         struct ast_xml_node *node = root_node;
1309
1310         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1311                 if (!strcasecmp(ast_xml_node_get_name(node), "syntax")) {
1312                         break;
1313                 }
1314         }
1315
1316         switch (xmldoc_get_syntax_type(type)) {
1317         case FUNCTION_SYNTAX:
1318                 syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
1319                 break;
1320         case COMMAND_SYNTAX:
1321                 syntax = xmldoc_get_syntax_cmd(node, name, 1);
1322                 break;
1323         case MANAGER_SYNTAX:
1324                 syntax = xmldoc_get_syntax_manager(node, name, "Action");
1325                 break;
1326         case MANAGER_EVENT_SYNTAX:
1327                 syntax = xmldoc_get_syntax_manager(node, name, "Event");
1328                 break;
1329         case CONFIG_OPTION_SYNTAX:
1330                 syntax = xmldoc_get_syntax_config_option(root_node, name);
1331                 break;
1332         case CONFIG_OBJECT_SYNTAX:
1333                 syntax = xmldoc_get_syntax_config_object(node, name);
1334                 break;
1335         default:
1336                 syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
1337         }
1338
1339         return syntax;
1340 }
1341
1342 char *ast_xmldoc_build_syntax(const char *type, const char *name, const char *module)
1343 {
1344         struct ast_xml_node *node;
1345
1346         node = xmldoc_get_node(type, name, module, documentation_language);
1347         if (!node) {
1348                 return NULL;
1349         }
1350
1351         return _ast_xmldoc_build_syntax(node, type, name);
1352 }
1353
1354 /*!
1355  * \internal
1356  * \brief Parse common internal elements.  This includes paragraphs, special
1357  *        tags, and information nodes.
1358  *
1359  * \param node The element to parse
1360  * \param tabs Add this string before the content of the parsed element.
1361  * \param posttabs Add this string after the content of the parsed element.
1362  * \param buffer This must be an already allocated ast_str. It will be used to
1363  *               store the result (if something has already been placed in the
1364  *               buffer, the parsed elements will be appended)
1365  *
1366  * \retval 1 if any data was appended to the buffer
1367  * \retval 2 if the data appended to the buffer contained a text paragraph
1368  * \retval 0 if no data was appended to the buffer
1369  */
1370 static int xmldoc_parse_common_elements(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer)
1371 {
1372         return (xmldoc_parse_para(node, tabs, posttabs, buffer)
1373                 || xmldoc_parse_specialtags(node, tabs, posttabs, buffer)
1374                 || xmldoc_parse_info(node, tabs, posttabs, buffer));
1375 }
1376
1377 /*!
1378  * \internal
1379  * \brief Parse a <para> element.
1380  *
1381  * \param node The <para> element pointer.
1382  * \param tabs Added this string before the content of the <para> element.
1383  * \param posttabs Added this string after the content of the <para> element.
1384  * \param buffer This must be an already allocated ast_str. It will be used
1385  *        to store the result (if already has something it will be appended to the current
1386  *        string).
1387  *
1388  * \retval 1 If 'node' is a named 'para'.
1389  * \retval 2 If data is appended in buffer.
1390  * \retval 0 on error.
1391  */
1392 static int xmldoc_parse_para(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer)
1393 {
1394         const char *tmptext;
1395         struct ast_xml_node *tmp;
1396         int ret = 0;
1397         struct ast_str *tmpstr;
1398
1399         if (!node || !ast_xml_node_get_children(node)) {
1400                 return ret;
1401         }
1402
1403         if (strcasecmp(ast_xml_node_get_name(node), "para")) {
1404                 return ret;
1405         }
1406
1407         ast_str_append(buffer, 0, "%s", tabs);
1408
1409         ret = 1;
1410
1411         for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
1412                 /* Get the text inside the <para> element and append it to buffer. */
1413                 tmptext = ast_xml_get_text(tmp);
1414                 if (tmptext) {
1415                         /* Strip \n etc. */
1416                         xmldoc_string_cleanup(tmptext, &tmpstr, 0);
1417                         ast_xml_free_text(tmptext);
1418                         if (tmpstr) {
1419                                 if (strcasecmp(ast_xml_node_get_name(tmp), "text")) {
1420                                         ast_str_append(buffer, 0, "<%s>%s</%s>", ast_xml_node_get_name(tmp),
1421                                                         ast_str_buffer(tmpstr), ast_xml_node_get_name(tmp));
1422                                 } else {
1423                                         ast_str_append(buffer, 0, "%s", ast_str_buffer(tmpstr));
1424                                 }
1425                                 ast_free(tmpstr);
1426                                 ret = 2;
1427                         }
1428                 }
1429         }
1430
1431         ast_str_append(buffer, 0, "%s", posttabs);
1432
1433         return ret;
1434 }
1435
1436 /*!
1437  * \internal
1438  * \brief Parse special elements defined in 'struct special_tags' special elements must have a <para> element inside them.
1439  *
1440  * \param fixnode special tag node pointer.
1441  * \param tabs put tabs before printing the node content.
1442  * \param posttabs put posttabs after printing node content.
1443  * \param buffer Output buffer, the special tags will be appended here.
1444  *
1445  * \retval 0 if no special element is parsed.
1446  * \retval 1 if a special element is parsed (data is appended to buffer).
1447  * \retval 2 if a special element is parsed and also a <para> element is parsed inside the specialtag.
1448  */
1449 static int xmldoc_parse_specialtags(struct ast_xml_node *fixnode, const char *tabs, const char *posttabs, struct ast_str **buffer)
1450 {
1451         struct ast_xml_node *node = fixnode;
1452         int ret = 0, i, count = 0;
1453
1454         if (!node || !ast_xml_node_get_children(node)) {
1455                 return ret;
1456         }
1457
1458         for (i = 0; i < ARRAY_LEN(special_tags); i++) {
1459                 if (strcasecmp(ast_xml_node_get_name(node), special_tags[i].tagname)) {
1460                         continue;
1461                 }
1462
1463                 ret = 1;
1464                 /* This is a special tag. */
1465
1466                 /* concat data */
1467                 if (!ast_strlen_zero(special_tags[i].init)) {
1468                         ast_str_append(buffer, 0, "%s%s", tabs, special_tags[i].init);
1469                 }
1470
1471                 /* parse <para> elements inside special tags. */
1472                 for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1473                         /* first <para> just print it without tabs at the begining. */
1474                         if ((xmldoc_parse_para(node, (!count ? "" : tabs), posttabs, buffer) == 2)
1475                                 || (xmldoc_parse_info(node, (!count ? "": tabs), posttabs, buffer) == 2)) {
1476                                 ret = 2;
1477                         }
1478                 }
1479
1480                 if (!ast_strlen_zero(special_tags[i].end)) {
1481                         ast_str_append(buffer, 0, "%s%s", special_tags[i].end, posttabs);
1482                 }
1483
1484                 break;
1485         }
1486
1487         return ret;
1488 }
1489
1490 /*!
1491  * \internal
1492  * \brief Parse an 'info' tag inside an element.
1493  *
1494  * \param node A pointer to the 'info' xml node.
1495  * \param tabs A string to be appended at the beginning of each line being printed
1496  *             inside 'buffer'
1497  * \param posttabs Add this string after the content of the <para> element, if one exists
1498  * \param String buffer to put values found inide the info element.
1499  *
1500  * \retval 2 if the information contained a para element, and it returned a value of 2
1501  * \retval 1 if information was put into the buffer
1502  * \retval 0 if no information was put into the buffer or error
1503  */
1504 static int xmldoc_parse_info(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer)
1505 {
1506         const char *tech;
1507         char *internaltabs;
1508         int internal_ret;
1509         int ret = 0;
1510
1511         if (strcasecmp(ast_xml_node_get_name(node), "info")) {
1512                 return ret;
1513         }
1514
1515         ast_asprintf(&internaltabs, "%s    ", tabs);
1516         if (!internaltabs) {
1517                 return ret;
1518         }
1519
1520         tech = ast_xml_get_attribute(node, "tech");
1521         if (tech) {
1522                 ast_str_append(buffer, 0, "%s<note>Technology: %s</note>\n", internaltabs, tech);
1523                 ast_xml_free_attr(tech);
1524         }
1525
1526         ret = 1;
1527
1528         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1529                 if (!strcasecmp(ast_xml_node_get_name(node), "enumlist")) {
1530                         xmldoc_parse_enumlist(node, internaltabs, buffer);
1531                 } else if ((internal_ret = xmldoc_parse_common_elements(node, internaltabs, posttabs, buffer))) {
1532                         if (internal_ret > ret) {
1533                                 ret = internal_ret;
1534                         }
1535                 }
1536         }
1537         ast_free(internaltabs);
1538
1539         return ret;
1540 }
1541
1542 /*!
1543  * \internal
1544  * \brief Parse an <argument> element from the xml documentation.
1545  *
1546  * \param fixnode Pointer to the 'argument' xml node.
1547  * \param insideparameter If we are parsing an <argument> inside a <parameter>.
1548  * \param paramtabs pre tabs if we are inside a parameter element.
1549  * \param tabs What to be printed before the argument name.
1550  * \param buffer Output buffer to put values found inside the <argument> element.
1551  *
1552  * \retval 1 If there is content inside the argument.
1553  * \retval 0 If the argument element is not parsed, or there is no content inside it.
1554  */
1555 static int xmldoc_parse_argument(struct ast_xml_node *fixnode, int insideparameter, const char *paramtabs, const char *tabs, struct ast_str **buffer)
1556 {
1557         struct ast_xml_node *node = fixnode;
1558         const char *argname;
1559         int count = 0, ret = 0;
1560
1561         if (!node || !ast_xml_node_get_children(node)) {
1562                 return ret;
1563         }
1564
1565         /* Print the argument names */
1566         argname = ast_xml_get_attribute(node, "name");
1567         if (!argname) {
1568                 return 0;
1569         }
1570         if (xmldoc_has_inside(node, "para") || xmldoc_has_inside(node, "info") || xmldoc_has_specialtags(node)) {
1571                 ast_str_append(buffer, 0, "%s%s%s", tabs, argname, (insideparameter ? "\n" : ""));
1572                 ast_xml_free_attr(argname);
1573         } else {
1574                 ast_xml_free_attr(argname);
1575                 return 0;
1576         }
1577
1578         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1579                 if (xmldoc_parse_common_elements(node, (insideparameter ? paramtabs : (!count ? " - " : tabs)), "\n", buffer) == 2) {
1580                         count++;
1581                         ret = 1;
1582                 }
1583         }
1584
1585         return ret;
1586 }
1587
1588 /*!
1589  * \internal
1590  * \brief Parse a <variable> node inside a <variablelist> node.
1591  *
1592  * \param node The variable node to parse.
1593  * \param tabs A string to be appended at the begining of the output that will be stored
1594  *        in buffer.
1595  * \param buffer This must be an already created ast_str. It will be used
1596  *        to store the result (if already has something it will be appended to the current
1597  *        string).
1598  *
1599  * \retval 0 if no data is appended.
1600  * \retval 1 if data is appended.
1601  */
1602 static int xmldoc_parse_variable(struct ast_xml_node *node, const char *tabs, struct ast_str **buffer)
1603 {
1604         struct ast_xml_node *tmp;
1605         const char *valname;
1606         const char *tmptext;
1607         struct ast_str *cleanstr;
1608         int ret = 0, printedpara=0;
1609
1610         for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
1611                 if (xmldoc_parse_common_elements(tmp, (ret ? tabs : ""), "\n", buffer)) {
1612                         printedpara = 1;
1613                         continue;
1614                 }
1615
1616                 if (strcasecmp(ast_xml_node_get_name(tmp), "value")) {
1617                         continue;
1618                 }
1619
1620                 /* Parse a <value> tag only. */
1621                 if (!printedpara) {
1622                         ast_str_append(buffer, 0, "\n");
1623                         printedpara = 1;
1624                 }
1625                 /* Parse each <value name='valuename'>desciption</value> */
1626                 valname = ast_xml_get_attribute(tmp, "name");
1627                 if (valname) {
1628                         ret = 1;
1629                         ast_str_append(buffer, 0, "%s<value>%s</value>", tabs, valname);
1630                         ast_xml_free_attr(valname);
1631                 }
1632                 tmptext = ast_xml_get_text(tmp);
1633                 /* Check inside this node for any explanation about its meaning. */
1634                 if (tmptext) {
1635                         /* Cleanup text. */
1636                         xmldoc_string_cleanup(tmptext, &cleanstr, 1);
1637                         ast_xml_free_text(tmptext);
1638                         if (cleanstr && ast_str_strlen(cleanstr) > 0) {
1639                                 ast_str_append(buffer, 0, ":%s", ast_str_buffer(cleanstr));
1640                         }
1641                         ast_free(cleanstr);
1642                 }
1643                 ast_str_append(buffer, 0, "\n");
1644         }
1645
1646         return ret;
1647 }
1648
1649 /*!
1650  * \internal
1651  * \brief Parse a <variablelist> node and put all the output inside 'buffer'.
1652  *
1653  * \param node The variablelist node pointer.
1654  * \param tabs A string to be appended at the begining of the output that will be stored
1655  *        in buffer.
1656  * \param buffer This must be an already created ast_str. It will be used
1657  *        to store the result (if already has something it will be appended to the current
1658  *        string).
1659  *
1660  * \retval 1 If a <variablelist> element is parsed.
1661  * \retval 0 On error.
1662  */
1663 static int xmldoc_parse_variablelist(struct ast_xml_node *node, const char *tabs, struct ast_str **buffer)
1664 {
1665         struct ast_xml_node *tmp;
1666         const char *varname;
1667         char *vartabs;
1668         int ret = 0;
1669
1670         if (!node || !ast_xml_node_get_children(node)) {
1671                 return ret;
1672         }
1673
1674         if (strcasecmp(ast_xml_node_get_name(node), "variablelist")) {
1675                 return ret;
1676         }
1677
1678         /* use this spacing (add 4 spaces) inside a variablelist node. */
1679         if (ast_asprintf(&vartabs, "%s    ", tabs) < 0) {
1680                 return ret;
1681         }
1682         for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
1683                 /* We can have a <para> element inside the variable list */
1684                 if (xmldoc_parse_common_elements(tmp, (ret ? tabs : ""), "\n", buffer)) {
1685                         ret = 1;
1686                         continue;
1687                 }
1688
1689                 if (!strcasecmp(ast_xml_node_get_name(tmp), "variable")) {
1690                         /* Store the variable name in buffer. */
1691                         varname = ast_xml_get_attribute(tmp, "name");
1692                         if (varname) {
1693                                 ast_str_append(buffer, 0, "%s<variable>%s</variable>: ", tabs, varname);
1694                                 ast_xml_free_attr(varname);
1695                                 /* Parse the <variable> possible values. */
1696                                 xmldoc_parse_variable(tmp, vartabs, buffer);
1697                                 ret = 1;
1698                         }
1699                 }
1700         }
1701
1702         ast_free(vartabs);
1703
1704         return ret;
1705 }
1706
1707 /*!
1708  * \internal
1709  * \brief Build seealso information for an item
1710  *
1711  * \param node  The seealso node to parse
1712  *
1713  * \note This method exists for when you already have the node.  This
1714  * prevents having to lock the documentation tree twice
1715  *
1716  * \retval A malloc'd character pointer to the seealso information of the item
1717  * \retval NULL on failure
1718  *
1719  * \since 11
1720  */
1721 static char *_ast_xmldoc_build_seealso(struct ast_xml_node *node)
1722 {
1723         char *output;
1724         struct ast_str *outputstr;
1725         const char *typename;
1726         const char *content;
1727         int first = 1;
1728
1729         /* Find the <see-also> node. */
1730         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1731                 if (!strcasecmp(ast_xml_node_get_name(node), "see-also")) {
1732                         break;
1733                 }
1734         }
1735
1736         if (!node || !ast_xml_node_get_children(node)) {
1737                 /* we couldnt find a <see-also> node. */
1738                 return NULL;
1739         }
1740
1741         /* prepare the output string. */
1742         outputstr = ast_str_create(128);
1743         if (!outputstr) {
1744                 return NULL;
1745         }
1746
1747         /* get into the <see-also> node. */
1748         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1749                 if (strcasecmp(ast_xml_node_get_name(node), "ref")) {
1750                         continue;
1751                 }
1752
1753                 /* parse the <ref> node. 'type' attribute is required. */
1754                 typename = ast_xml_get_attribute(node, "type");
1755                 if (!typename) {
1756                         continue;
1757                 }
1758                 content = ast_xml_get_text(node);
1759                 if (!content) {
1760                         ast_xml_free_attr(typename);
1761                         continue;
1762                 }
1763                 if (!strcasecmp(typename, "application")) {
1764                         ast_str_append(&outputstr, 0, "%s%s()", (first ? "" : ", "), content);
1765                 } else if (!strcasecmp(typename, "function")) {
1766                         ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content);
1767                 } else if (!strcasecmp(typename, "astcli")) {
1768                         ast_str_append(&outputstr, 0, "%s<astcli>%s</astcli>", (first ? "" : ", "), content);
1769                 } else {
1770                         ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content);
1771                 }
1772                 first = 0;
1773                 ast_xml_free_text(content);
1774                 ast_xml_free_attr(typename);
1775         }
1776
1777         output = ast_strdup(ast_str_buffer(outputstr));
1778         ast_free(outputstr);
1779
1780         return output;
1781 }
1782
1783 char *ast_xmldoc_build_seealso(const char *type, const char *name, const char *module)
1784 {
1785         char *output;
1786         struct ast_xml_node *node;
1787
1788         if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
1789                 return NULL;
1790         }
1791
1792         /* get the application/function root node. */
1793         node = xmldoc_get_node(type, name, module, documentation_language);
1794         if (!node || !ast_xml_node_get_children(node)) {
1795                 return NULL;
1796         }
1797
1798         output = _ast_xmldoc_build_seealso(node);
1799
1800         return output;
1801 }
1802
1803 /*!
1804  * \internal
1805  * \brief Parse a <enum> node.
1806  *
1807  * \param fixnode An ast_xml_node pointer to the <enum> node.
1808  * \param buffer The output buffer.
1809  *
1810  * \retval 0 if content is not found inside the enum element (data is not appended to buffer).
1811  * \retval 1 if content is found and data is appended to buffer.
1812  */
1813 static int xmldoc_parse_enum(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
1814 {
1815         struct ast_xml_node *node = fixnode;
1816         int ret = 0;
1817         char *optiontabs;
1818
1819         if (ast_asprintf(&optiontabs, "%s    ", tabs) < 0) {
1820                 return ret;
1821         }
1822
1823         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1824                 if (xmldoc_parse_common_elements(node, (ret ? tabs : " - "), "\n", buffer)) {
1825                         ret = 1;
1826                 }
1827
1828                 xmldoc_parse_enumlist(node, optiontabs, buffer);
1829         }
1830
1831         ast_free(optiontabs);
1832
1833         return ret;
1834 }
1835
1836 /*!
1837  * \internal
1838  * \brief Parse a <enumlist> node.
1839  *
1840  * \param fixnode As ast_xml pointer to the <enumlist> node.
1841  * \param buffer The ast_str output buffer.
1842  *
1843  * \retval 0 if no <enumlist> node was parsed.
1844  * \retval 1 if a <enumlist> node was parsed.
1845  */
1846 static int xmldoc_parse_enumlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
1847 {
1848         struct ast_xml_node *node = fixnode;
1849         const char *enumname;
1850         int ret = 0;
1851
1852         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1853                 if (strcasecmp(ast_xml_node_get_name(node), "enum")) {
1854                         continue;
1855                 }
1856
1857                 enumname = ast_xml_get_attribute(node, "name");
1858                 if (enumname) {
1859                         ast_str_append(buffer, 0, "%s<enum>%s</enum>", tabs, enumname);
1860                         ast_xml_free_attr(enumname);
1861
1862                         /* parse only enum elements inside a enumlist node. */
1863                         if ((xmldoc_parse_enum(node, tabs, buffer))) {
1864                                 ret = 1;
1865                         } else {
1866                                 ast_str_append(buffer, 0, "\n");
1867                         }
1868                 }
1869         }
1870         return ret;
1871 }
1872
1873 /*!
1874  * \internal
1875  * \brief Parse an <option> node.
1876  *
1877  * \param fixnode An ast_xml pointer to the <option> node.
1878  * \param tabs A string to be appended at the begining of each line being added to the
1879  *             buffer string.
1880  * \param buffer The output buffer.
1881  *
1882  * \retval 0 if no option node is parsed.
1883  * \retval 1 if an option node is parsed.
1884  */
1885 static int xmldoc_parse_option(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
1886 {
1887         struct ast_xml_node *node;
1888         int ret = 0;
1889         char *optiontabs;
1890
1891         if (ast_asprintf(&optiontabs, "%s    ", tabs) < 0) {
1892                 return ret;
1893         }
1894         for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
1895                 if (!strcasecmp(ast_xml_node_get_name(node), "argument")) {
1896                         /* if this is the first data appended to buffer, print a \n*/
1897                         if (!ret && ast_xml_node_get_children(node)) {
1898                                 /* print \n */
1899                                 ast_str_append(buffer, 0, "\n");
1900                         }
1901                         if (xmldoc_parse_argument(node, 0, NULL, optiontabs, buffer)) {
1902                                 ret = 1;
1903                         }
1904                         continue;
1905                 }
1906
1907                 if (xmldoc_parse_common_elements(node, (ret ? tabs :  ""), "\n", buffer)) {
1908                         ret = 1;
1909                 }
1910
1911                 xmldoc_parse_variablelist(node, optiontabs, buffer);
1912
1913                 xmldoc_parse_enumlist(node, optiontabs, buffer);
1914         }
1915         ast_free(optiontabs);
1916
1917         return ret;
1918 }
1919
1920 /*!
1921  * \internal
1922  * \brief Parse an <optionlist> element from the xml documentation.
1923  *
1924  * \param fixnode Pointer to the optionlist xml node.
1925  * \param tabs A string to be appended at the begining of each line being added to the
1926  *             buffer string.
1927  * \param buffer Output buffer to put what is inside the optionlist tag.
1928  */
1929 static void xmldoc_parse_optionlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
1930 {
1931         struct ast_xml_node *node;
1932         const char *optname, *hasparams;
1933         char *optionsyntax;
1934         int optparams;
1935
1936         for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
1937                 /* Start appending every option tag. */
1938                 if (strcasecmp(ast_xml_node_get_name(node), "option")) {
1939                         continue;
1940                 }
1941
1942                 /* Get the option name. */
1943                 optname = ast_xml_get_attribute(node, "name");
1944                 if (!optname) {
1945                         continue;
1946                 }
1947
1948                 optparams = 1;
1949                 hasparams = ast_xml_get_attribute(node, "hasparams");
1950                 if (hasparams && !strcasecmp(hasparams, "optional")) {
1951                         optparams = 2;
1952                 }
1953
1954                 optionsyntax = xmldoc_get_syntax_fun(node, optname, "argument", 0, optparams);
1955                 if (!optionsyntax) {
1956                         ast_xml_free_attr(optname);
1957                         ast_xml_free_attr(hasparams);
1958                         continue;
1959                 }
1960
1961                 ast_str_append(buffer, 0, "%s%s: ", tabs, optionsyntax);
1962
1963                 if (!xmldoc_parse_option(node, tabs, buffer)) {
1964                         ast_str_append(buffer, 0, "\n");
1965                 }
1966                 ast_str_append(buffer, 0, "\n");
1967                 ast_xml_free_attr(optname);
1968                 ast_xml_free_attr(hasparams);
1969                 ast_free(optionsyntax);
1970         }
1971 }
1972
1973 /*!
1974  * \internal
1975  * \brief Parse a 'parameter' tag inside a syntax element.
1976  *
1977  * \param fixnode A pointer to the 'parameter' xml node.
1978  * \param tabs A string to be appended at the beginning of each line being printed inside
1979  *             'buffer'.
1980  * \param buffer String buffer to put values found inside the parameter element.
1981  */
1982 static void xmldoc_parse_parameter(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
1983 {
1984         const char *paramname;
1985         struct ast_xml_node *node = fixnode;
1986         int hasarguments, printed = 0;
1987         char *internaltabs;
1988
1989         if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
1990                 return;
1991         }
1992
1993         hasarguments = xmldoc_has_inside(node, "argument");
1994         if (!(paramname = ast_xml_get_attribute(node, "name"))) {
1995                 /* parameter MUST have an attribute name. */
1996                 return;
1997         }
1998
1999         if (ast_asprintf(&internaltabs, "%s    ", tabs) < 0) {
2000                 ast_xml_free_attr(paramname);
2001                 return;
2002         }
2003
2004         if (!hasarguments && xmldoc_has_nodes(node)) {
2005                 ast_str_append(buffer, 0, "%s\n", paramname);
2006                 ast_xml_free_attr(paramname);
2007                 printed = 1;
2008         }
2009
2010         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
2011                 if (!strcasecmp(ast_xml_node_get_name(node), "optionlist")) {
2012                         xmldoc_parse_optionlist(node, internaltabs, buffer);
2013                 } else if (!strcasecmp(ast_xml_node_get_name(node), "enumlist")) {
2014                         xmldoc_parse_enumlist(node, internaltabs, buffer);
2015                 } else if (!strcasecmp(ast_xml_node_get_name(node), "argument")) {
2016                         xmldoc_parse_argument(node, 1, internaltabs, (!hasarguments ? "        " : ""), buffer);
2017                 } else if (!strcasecmp(ast_xml_node_get_name(node), "para")) {
2018                         if (!printed) {
2019                                 ast_str_append(buffer, 0, "%s\n", paramname);
2020                                 ast_xml_free_attr(paramname);
2021                                 printed = 1;
2022                         }
2023                         if (xmldoc_parse_para(node, internaltabs, "\n", buffer)) {
2024                                 /* If anything ever goes in below this condition before the continue below,
2025                                  * we should probably continue immediately. */
2026                                 continue;
2027                         }
2028                         continue;
2029                 } else if (!strcasecmp(ast_xml_node_get_name(node), "info")) {
2030                         if (!printed) {
2031                                 ast_str_append(buffer, 0, "%s\n", paramname);
2032                                 ast_xml_free_attr(paramname);
2033                                 printed = 1;
2034                         }
2035                         if (xmldoc_parse_info(node, internaltabs, "\n", buffer)) {
2036                                 /* If anything ever goes in below this condition before the continue below,
2037                                  * we should probably continue immediately. */
2038                                 continue;
2039                         }
2040                         continue;
2041                 } else if ((xmldoc_parse_specialtags(node, internaltabs, "\n", buffer))) {
2042                         continue;
2043                 }
2044         }
2045         if (!printed) {
2046                 ast_xml_free_attr(paramname);
2047         }
2048         ast_free(internaltabs);
2049 }
2050
2051 /*!
2052  * \internal
2053  * \brief Build the arguments for an item
2054  *
2055  * \param node  The arguments node to parse
2056  *
2057  * \note This method exists for when you already have the node.  This
2058  * prevents having to lock the documentation tree twice
2059  *
2060  * \retval A malloc'd character pointer to the arguments for the item
2061  * \retval NULL on failure
2062  *
2063  * \since 11
2064  */
2065 static char *_ast_xmldoc_build_arguments(struct ast_xml_node *node)
2066 {
2067         char *retstr = NULL;
2068         struct ast_str *ret;
2069
2070         ret = ast_str_create(128);
2071         if (!ret) {
2072                 return NULL;
2073         }
2074
2075         /* Find the syntax field. */
2076         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
2077                 if (!strcasecmp(ast_xml_node_get_name(node), "syntax")) {
2078                         break;
2079                 }
2080         }
2081
2082         if (!node || !ast_xml_node_get_children(node)) {
2083                 /* We couldn't find the syntax node. */
2084                 ast_free(ret);
2085                 return NULL;
2086         }
2087
2088         for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
2089                 xmldoc_parse_parameter(node, "", &ret);
2090         }
2091
2092         if (ast_str_strlen(ret) > 0) {
2093                 /* remove last '\n' */
2094                 char *buf = ast_str_buffer(ret);
2095                 if (buf[ast_str_strlen(ret) - 1] == '\n') {
2096                         ast_str_truncate(ret, -1);
2097                 }
2098                 retstr = ast_strdup(ast_str_buffer(ret));
2099         }
2100         ast_free(ret);
2101
2102         return retstr;
2103 }
2104
2105 char *ast_xmldoc_build_arguments(const char *type, const char *name, const char *module)
2106 {
2107         struct ast_xml_node *node;
2108
2109         if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
2110                 return NULL;
2111         }
2112
2113         node = xmldoc_get_node(type, name, module, documentation_language);
2114
2115         if (!node || !ast_xml_node_get_children(node)) {
2116                 return NULL;
2117         }
2118
2119         return _ast_xmldoc_build_arguments(node);
2120 }
2121
2122 /*!
2123  * \internal
2124  * \brief Return the string within a node formatted with <para> and <variablelist> elements.
2125  *
2126  * \param node Parent node where content resides.
2127  * \param raw If set, return the node's content without further processing.
2128  * \param raw_wrap Wrap raw text.
2129  *
2130  * \retval NULL on error
2131  * \retval Node content on success.
2132  */
2133 static struct ast_str *xmldoc_get_formatted(struct ast_xml_node *node, int raw_output, int raw_wrap)
2134 {
2135         struct ast_xml_node *tmp;
2136         const char *notcleanret, *tmpstr;
2137         struct ast_str *ret;
2138
2139         if (raw_output) {
2140                 /* xmldoc_string_cleanup will allocate the ret object */
2141                 notcleanret = ast_xml_get_text(node);
2142                 tmpstr = notcleanret;
2143                 xmldoc_string_cleanup(ast_skip_blanks(notcleanret), &ret, 0);
2144                 ast_xml_free_text(tmpstr);
2145         } else {
2146                 ret = ast_str_create(128);
2147                 for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
2148                         /* if found, parse a <para> element. */
2149                         if (xmldoc_parse_common_elements(tmp, "", "\n", &ret)) {
2150                                 continue;
2151                         }
2152                         /* if found, parse a <variablelist> element. */
2153                         xmldoc_parse_variablelist(tmp, "", &ret);
2154                         xmldoc_parse_enumlist(tmp, "    ", &ret);
2155                 }
2156                 /* remove last '\n' */
2157                 /* XXX Don't modify ast_str internals manually */
2158                 tmpstr = ast_str_buffer(ret);
2159                 if (tmpstr[ast_str_strlen(ret) - 1] == '\n') {
2160                         ast_str_truncate(ret, -1);
2161                 }
2162         }
2163         return ret;
2164 }
2165
2166 /*!
2167  * \internal
2168  * \brief Get the content of a field (synopsis, description, etc) from an asterisk document tree node
2169  *
2170  * \param node The node to obtain the information from
2171  * \param var Name of field to return (synopsis, description, etc).
2172  * \param raw Field only contains text, no other elements inside it.
2173  *
2174  * \retval NULL On error.
2175  * \retval Field text content on success.
2176  * \since 11
2177  */
2178 static char *_xmldoc_build_field(struct ast_xml_node *node, const char *var, int raw)
2179 {
2180         char *ret = NULL;
2181         struct ast_str *formatted;
2182
2183         node = ast_xml_find_element(ast_xml_node_get_children(node), var, NULL, NULL);
2184
2185         if (!node || !ast_xml_node_get_children(node)) {
2186                 ast_debug(1, "Cannot find variable '%s' in tree\n", var);
2187                 return ret;
2188         }
2189
2190         formatted = xmldoc_get_formatted(node, raw, raw);
2191         if (ast_str_strlen(formatted) > 0) {
2192                 ret = ast_strdup(ast_str_buffer(formatted));
2193         }
2194         ast_free(formatted);
2195
2196         return ret;
2197 }
2198
2199 /*!
2200  * \internal
2201  * \brief Get the content of a field (synopsis, description, etc) from an asterisk document tree
2202  *
2203  * \param type Type of element (application, function, ...).
2204  * \param name Name of element (Dial, Echo, Playback, ...).
2205  * \param var Name of field to return (synopsis, description, etc).
2206  * \param module
2207  * \param raw Field only contains text, no other elements inside it.
2208  *
2209  * \retval NULL On error.
2210  * \retval Field text content on success.
2211  */
2212 static char *xmldoc_build_field(const char *type, const char *name, const char *module, const char *var, int raw)
2213 {
2214         struct ast_xml_node *node;
2215
2216         if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
2217                 ast_log(LOG_ERROR, "Tried to look in XML tree with faulty values.\n");
2218                 return NULL;
2219         }
2220
2221         node = xmldoc_get_node(type, name, module, documentation_language);
2222
2223         if (!node) {
2224                 ast_log(LOG_WARNING, "Couldn't find %s %s in XML documentation\n", type, name);
2225                 return NULL;
2226         }
2227
2228         return _xmldoc_build_field(node, var, raw);
2229 }
2230
2231 /*!
2232  * \internal
2233  * \brief Build the synopsis for an item
2234  *
2235  * \param node The synopsis node
2236  *
2237  * \note This method exists for when you already have the node.  This
2238  * prevents having to lock the documentation tree twice
2239  *
2240  * \retval A malloc'd character pointer to the synopsis information
2241  * \retval NULL on failure
2242  * \since 11
2243  */
2244 static char *_ast_xmldoc_build_synopsis(struct ast_xml_node *node)
2245 {
2246         return _xmldoc_build_field(node, "synopsis", 1);
2247 }
2248
2249 char *ast_xmldoc_build_synopsis(const char *type, const char *name, const char *module)
2250 {
2251         return xmldoc_build_field(type, name, module, "synopsis", 1);
2252 }
2253
2254 /*!
2255  * \internal
2256  * \brief Build the descripton for an item
2257  *
2258  * \param node  The description node to parse
2259  *
2260  * \note This method exists for when you already have the node.  This
2261  * prevents having to lock the documentation tree twice
2262  *
2263  * \retval A malloc'd character pointer to the arguments for the item
2264  * \retval NULL on failure
2265  * \since 11
2266  */
2267 static char *_ast_xmldoc_build_description(struct ast_xml_node *node)
2268 {
2269         return _xmldoc_build_field(node, "description", 0);
2270 }
2271
2272 char *ast_xmldoc_build_description(const char *type, const char *name, const char *module)
2273 {
2274         return xmldoc_build_field(type, name, module, "description", 0);
2275 }
2276
2277 /*!
2278  * \internal
2279  * \brief ast_xml_doc_item ao2 destructor
2280  * \since 11
2281  */
2282 static void ast_xml_doc_item_destructor(void *obj)
2283 {
2284         struct ast_xml_doc_item *doc = obj;
2285
2286         if (!doc) {
2287                 return;
2288         }
2289
2290         ast_free(doc->syntax);
2291         ast_free(doc->seealso);
2292         ast_free(doc->arguments);
2293         ast_free(doc->synopsis);
2294         ast_free(doc->description);
2295         ast_string_field_free_memory(doc);
2296
2297         if (doc->next) {
2298                 ao2_ref(doc->next, -1);
2299                 doc->next = NULL;
2300         }
2301 }
2302
2303 /*!
2304  * \internal
2305  * \brief Create an ao2 ref counted ast_xml_doc_item
2306  *
2307  * \param name The name of the item
2308  * \param type The item's source type
2309  * \since 11
2310  */
2311 static struct ast_xml_doc_item *ast_xml_doc_item_alloc(const char *name, const char *type)
2312 {
2313         struct ast_xml_doc_item *item;
2314
2315         if (!(item = ao2_alloc(sizeof(*item), ast_xml_doc_item_destructor))) {
2316                 ast_log(AST_LOG_ERROR, "Failed to allocate memory for ast_xml_doc_item instance\n");
2317                 return NULL;
2318         }
2319
2320         if (   !(item->syntax = ast_str_create(128))
2321                 || !(item->seealso = ast_str_create(128))
2322                 || !(item->arguments = ast_str_create(128))
2323                 || !(item->synopsis = ast_str_create(128))
2324                 || !(item->description = ast_str_create(128))) {
2325                 ast_log(AST_LOG_ERROR, "Failed to allocate strings for ast_xml_doc_item instance\n");
2326                 goto ast_xml_doc_item_failure;
2327         }
2328
2329         if (ast_string_field_init(item, 64)) {
2330                 ast_log(AST_LOG_ERROR, "Failed to initialize string field for ast_xml_doc_item instance\n");
2331                 goto ast_xml_doc_item_failure;
2332         }
2333         ast_string_field_set(item, name, name);
2334         ast_string_field_set(item, type, type);
2335
2336         return item;
2337
2338 ast_xml_doc_item_failure:
2339         ao2_ref(item, -1);
2340         return NULL;
2341 }
2342
2343 /*!
2344  * \internal
2345  * \brief ao2 item hash function for ast_xml_doc_item
2346  * \since 11
2347  */
2348 static int ast_xml_doc_item_hash(const void *obj, const int flags)
2349 {
2350         const struct ast_xml_doc_item *item = obj;
2351         const char *name = (flags & OBJ_KEY) ? obj : item->name;
2352         return ast_str_case_hash(name);
2353 }
2354
2355 /*!
2356  * \internal
2357  * \brief ao2 item comparison function for ast_xml_doc_item
2358  * \since 11
2359  */
2360 static int ast_xml_doc_item_cmp(void *obj, void *arg, int flags)
2361 {
2362         struct ast_xml_doc_item *left = obj;
2363         struct ast_xml_doc_item *right = arg;
2364         const char *match = (flags & OBJ_KEY) ? arg : right->name;
2365         return strcasecmp(left->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
2366 }
2367
2368 /*!
2369  * \internal
2370  * \brief Build an XML documentation item
2371  *
2372  * \param node The root node for the item
2373  * \param name The name of the item
2374  * \param type The item's source type
2375  *
2376  * \retval NULL on failure
2377  * \retval An ao2 ref counted object
2378  * \since 11
2379  */
2380 static struct ast_xml_doc_item *xmldoc_build_documentation_item(struct ast_xml_node *node, const char *name, const char *type)
2381 {
2382         struct ast_xml_doc_item *item;
2383         char *syntax;
2384         char *seealso;
2385         char *arguments;
2386         char *synopsis;
2387         char *description;
2388
2389         if (!(item = ast_xml_doc_item_alloc(name, type))) {
2390                 return NULL;
2391         }
2392         item->node = node;
2393
2394         syntax = _ast_xmldoc_build_syntax(node, type, name);
2395         seealso = _ast_xmldoc_build_seealso(node);
2396         arguments = _ast_xmldoc_build_arguments(node);
2397         synopsis = _ast_xmldoc_build_synopsis(node);
2398         description = _ast_xmldoc_build_description(node);
2399
2400         if (syntax) {
2401                 ast_str_set(&item->syntax, 0, "%s", syntax);
2402         }
2403         if (seealso) {
2404                 ast_str_set(&item->seealso, 0, "%s", seealso);
2405         }
2406         if (arguments) {
2407                 ast_str_set(&item->arguments, 0, "%s", arguments);
2408         }
2409         if (synopsis) {
2410                 ast_str_set(&item->synopsis, 0, "%s", synopsis);
2411         }
2412         if (description) {
2413                 ast_str_set(&item->description, 0, "%s", description);
2414         }
2415
2416         ast_free(syntax);
2417         ast_free(seealso);
2418         ast_free(arguments);
2419         ast_free(synopsis);
2420         ast_free(description);
2421
2422         return item;
2423 }
2424
2425 struct ast_xml_xpath_results *__attribute__((format(printf, 1, 2))) ast_xmldoc_query(const char *fmt, ...)
2426 {
2427         struct ast_xml_xpath_results *results = NULL;
2428         struct documentation_tree *doctree;
2429         RAII_VAR(struct ast_str *, xpath_str, ast_str_create(128), ast_free);
2430         va_list ap;
2431
2432         if (!xpath_str) {
2433                 return NULL;
2434         }
2435
2436         va_start(ap, fmt);
2437         ast_str_set_va(&xpath_str, 0, fmt, ap);
2438         va_end(ap);
2439
2440         AST_RWLIST_RDLOCK(&xmldoc_tree);
2441         AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
2442                 if (!(results = ast_xml_query(doctree->doc, ast_str_buffer(xpath_str)))) {
2443                         continue;
2444                 }
2445                 break;
2446         }
2447         AST_RWLIST_UNLOCK(&xmldoc_tree);
2448
2449         return results;
2450 }
2451
2452 static void build_config_docs(struct ast_xml_node *cur, struct ast_xml_doc_item **tail)
2453 {
2454         struct ast_xml_node *iter;
2455         struct ast_xml_doc_item *item;
2456
2457         for (iter = ast_xml_node_get_children(cur); iter; iter = ast_xml_node_get_next(iter)) {
2458                 const char *iter_name;
2459                 if (strncasecmp(ast_xml_node_get_name(iter), "config", 6)) {
2460                         continue;
2461                 }
2462                 iter_name = ast_xml_get_attribute(iter, "name");
2463                 /* Now add all of the child config-related items to the list */
2464                 if (!(item = xmldoc_build_documentation_item(iter, iter_name, ast_xml_node_get_name(iter)))) {
2465                         ast_log(LOG_ERROR, "Could not build documentation for '%s:%s'\n", ast_xml_node_get_name(iter), iter_name);
2466                         ast_xml_free_attr(iter_name);
2467                         break;
2468                 }
2469                 ast_xml_free_attr(iter_name);
2470                 if (!strcasecmp(ast_xml_node_get_name(iter), "configOption")) {
2471                         const char *name = ast_xml_get_attribute(cur, "name");
2472                         ast_string_field_set(item, ref, name);
2473                         ast_xml_free_attr(name);
2474                 }
2475                 (*tail)->next = item;
2476                 *tail = (*tail)->next;
2477                 build_config_docs(iter, tail);
2478         }
2479 }
2480
2481 int ast_xmldoc_regenerate_doc_item(struct ast_xml_doc_item *item)
2482 {
2483         const char *name;
2484         char *syntax;
2485         char *seealso;
2486         char *arguments;
2487         char *synopsis;
2488         char *description;
2489
2490         if (!item || !item->node) {
2491                 return -1;
2492         }
2493
2494         name = ast_xml_get_attribute(item->node, "name");
2495         if (!name) {
2496                 return -1;
2497         }
2498
2499         syntax = _ast_xmldoc_build_syntax(item->node, item->type, name);
2500         seealso = _ast_xmldoc_build_seealso(item->node);
2501         arguments = _ast_xmldoc_build_arguments(item->node);
2502         synopsis = _ast_xmldoc_build_synopsis(item->node);
2503         description = _ast_xmldoc_build_description(item->node);
2504
2505         if (syntax) {
2506                 ast_str_set(&item->syntax, 0, "%s", syntax);
2507         }
2508         if (seealso) {
2509                 ast_str_set(&item->seealso, 0, "%s", seealso);
2510         }
2511         if (arguments) {
2512                 ast_str_set(&item->arguments, 0, "%s", arguments);
2513         }
2514         if (synopsis) {
2515                 ast_str_set(&item->synopsis, 0, "%s", synopsis);
2516         }
2517         if (description) {
2518                 ast_str_set(&item->description, 0, "%s", description);
2519         }
2520
2521         ast_free(syntax);
2522         ast_free(seealso);
2523         ast_free(arguments);
2524         ast_free(synopsis);
2525         ast_free(description);
2526         ast_xml_free_attr(name);
2527         return 0;
2528 }
2529
2530 struct ao2_container *ast_xmldoc_build_documentation(const char *type)
2531 {
2532         struct ao2_container *docs;
2533         struct ast_xml_doc_item *item = NULL, *root = NULL;
2534         struct ast_xml_node *node = NULL, *instance = NULL;
2535         struct documentation_tree *doctree;
2536         const char *name;
2537
2538         if (!(docs = ao2_container_alloc(127, ast_xml_doc_item_hash, ast_xml_doc_item_cmp))) {
2539                 ast_log(AST_LOG_ERROR, "Failed to create container for xml document item instances\n");
2540                 return NULL;
2541         }
2542
2543         AST_RWLIST_RDLOCK(&xmldoc_tree);
2544         AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
2545                 /* the core xml documents have priority over thirdparty document. */
2546                 node = ast_xml_get_root(doctree->doc);
2547                 if (!node) {
2548                         break;
2549                 }
2550
2551                 for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
2552                         /* Ignore empty nodes or nodes that aren't of the type requested */
2553                         if (!ast_xml_node_get_children(node) || strcasecmp(ast_xml_node_get_name(node), type)) {
2554                                 continue;
2555                         }
2556                         name = ast_xml_get_attribute(node, "name");
2557                         if (!name) {
2558                                 continue;
2559                         }
2560
2561                         switch (xmldoc_get_syntax_type(type)) {
2562                         case MANAGER_EVENT_SYNTAX:
2563                                 for (instance = ast_xml_node_get_children(node); instance; instance = ast_xml_node_get_next(instance)) {
2564                                         struct ast_xml_doc_item *temp;
2565                                         if (!ast_xml_node_get_children(instance) || strcasecmp(ast_xml_node_get_name(instance), "managerEventInstance")) {
2566                                                 continue;
2567                                         }
2568                                         temp = xmldoc_build_documentation_item(instance, name, type);
2569                                         if (!temp) {
2570                                                 break;
2571                                         }
2572                                         if (!item) {
2573                                                 item = temp;
2574                                                 root = item;
2575                                         } else {
2576                                                 item->next = temp;
2577                                                 item = temp;
2578                                         }
2579                                 }
2580                                 item = root;
2581                                 break;
2582                         case CONFIG_INFO_SYNTAX:
2583                         {
2584                                 struct ast_xml_doc_item *tail;
2585                                 RAII_VAR(const char *, name, ast_xml_get_attribute(node, "name"), ast_xml_free_attr);
2586                                 if (item || !ast_xml_node_get_children(node) || strcasecmp(ast_xml_node_get_name(node), "configInfo")) {
2587                                         break;
2588                                 }
2589                                 if (!(item = xmldoc_build_documentation_item(node, name, "configInfo"))) {
2590                                         break;
2591                                 }
2592                                 tail = item;
2593                                 build_config_docs(node, &tail);
2594                                 break;
2595                         }
2596                         default:
2597                                 item = xmldoc_build_documentation_item(node, name, type);
2598                         }
2599                         ast_xml_free_attr(name);
2600
2601                         if (item) {
2602                                 ao2_link(docs, item);
2603                                 ao2_t_ref(item, -1, "Dispose of creation ref");
2604                                 item = NULL;
2605                         }
2606                 }
2607         }
2608         AST_RWLIST_UNLOCK(&xmldoc_tree);
2609
2610         return docs;
2611 }
2612
2613 int ast_xmldoc_regenerate_doc_item(struct ast_xml_doc_item *item);
2614
2615
2616 #if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
2617 static int xml_pathmatch(char *xmlpattern, int xmlpattern_maxlen, glob_t *globbuf)
2618 {
2619         int globret;
2620
2621         snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/thirdparty/*-%s.xml",
2622                 ast_config_AST_DATA_DIR, documentation_language);
2623         if((globret = glob(xmlpattern, GLOB_NOCHECK, NULL, globbuf))) {
2624                 return globret;
2625         }
2626
2627         snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/thirdparty/*-%.2s_??.xml",
2628                 ast_config_AST_DATA_DIR, documentation_language);
2629         if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
2630                 return globret;
2631         }
2632
2633         snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/thirdparty/*-%s.xml",
2634                 ast_config_AST_DATA_DIR, default_documentation_language);
2635         if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
2636                 return globret;
2637         }
2638
2639         snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/*-%s.xml",
2640                 ast_config_AST_DATA_DIR, documentation_language);
2641         if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
2642                 return globret;
2643         }
2644
2645         snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/*-%.2s_??.xml",
2646                 ast_config_AST_DATA_DIR, documentation_language);
2647         if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
2648                 return globret;
2649         }
2650
2651         snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/*-%s.xml",
2652                 ast_config_AST_DATA_DIR, default_documentation_language);
2653         globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf);
2654
2655         return globret;
2656 }
2657 #endif
2658
2659 static char *handle_dump_docs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2660 {
2661         struct documentation_tree *doctree;
2662         FILE *f;
2663
2664         switch (cmd) {
2665         case CLI_INIT:
2666                 e->command = "xmldoc dump";
2667                 e->usage =
2668                         "Usage: xmldoc dump <filename>\n"
2669                         "  Dump XML documentation to a file\n";
2670                 return NULL;
2671         case CLI_GENERATE:
2672                 return NULL;
2673         }
2674
2675         if (a->argc != 3) {
2676                 return CLI_SHOWUSAGE;
2677         }
2678         if (!(f = fopen(a->argv[2], "w"))) {
2679                 ast_log(LOG_ERROR, "Could not open file '%s': %s\n", a->argv[2], strerror(errno));
2680                 return CLI_FAILURE;
2681         }
2682         AST_RWLIST_RDLOCK(&xmldoc_tree);
2683         AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
2684                 ast_xml_doc_dump_file(f, doctree->doc);
2685         }
2686         AST_RWLIST_UNLOCK(&xmldoc_tree);
2687         fclose(f);
2688         return CLI_SUCCESS;
2689 }
2690
2691 static struct ast_cli_entry cli_dump_xmldocs = AST_CLI_DEFINE(handle_dump_docs, "Dump the XML docs to the specified file");
2692
2693 /*! \brief Close and unload XML documentation. */
2694 static void xmldoc_unload_documentation(void)
2695 {
2696         struct documentation_tree *doctree;
2697
2698         ast_cli_unregister(&cli_dump_xmldocs);
2699
2700         AST_RWLIST_WRLOCK(&xmldoc_tree);
2701         while ((doctree = AST_RWLIST_REMOVE_HEAD(&xmldoc_tree, entry))) {
2702                 ast_free(doctree->filename);
2703                 ast_xml_close(doctree->doc);
2704                 ast_free(doctree);
2705         }
2706         AST_RWLIST_UNLOCK(&xmldoc_tree);
2707
2708         ast_xml_finish();
2709 }
2710
2711 int ast_xmldoc_load_documentation(void)
2712 {
2713         struct ast_xml_node *root_node;
2714         struct ast_xml_doc *tmpdoc;
2715         struct documentation_tree *doc_tree;
2716         char *xmlpattern;
2717         struct ast_config *cfg = NULL;
2718         struct ast_variable *var = NULL;
2719         struct ast_flags cnfflags = { 0 };
2720         int globret, i, dup, duplicate;
2721         glob_t globbuf;
2722 #if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
2723         int xmlpattern_maxlen;
2724 #endif
2725
2726         /* setup default XML documentation language */
2727         snprintf(documentation_language, sizeof(documentation_language), default_documentation_language);
2728
2729         if ((cfg = ast_config_load2("asterisk.conf", "" /* core can't reload */, cnfflags)) && cfg != CONFIG_STATUS_FILEINVALID) {
2730                 for (var = ast_variable_browse(cfg, "options"); var; var = var->next) {
2731                         if (!strcasecmp(var->name, "documentation_language")) {
2732                                 if (!ast_strlen_zero(var->value)) {
2733                                         snprintf(documentation_language, sizeof(documentation_language), "%s", var->value);
2734                                 }
2735                         }
2736                 }
2737                 ast_config_destroy(cfg);
2738         }
2739
2740         /* initialize the XML library. */
2741         ast_xml_init();
2742
2743         ast_cli_register(&cli_dump_xmldocs);
2744         /* register function to be run when asterisk finish. */
2745         ast_register_atexit(xmldoc_unload_documentation);
2746
2747         globbuf.gl_offs = 0;    /* slots to reserve in gl_pathv */
2748
2749 #if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
2750         xmlpattern_maxlen = strlen(ast_config_AST_DATA_DIR) + strlen("/documentation/thirdparty") + strlen("/*-??_??.xml") + 1;
2751         xmlpattern = ast_malloc(xmlpattern_maxlen);
2752         globret = xml_pathmatch(xmlpattern, xmlpattern_maxlen, &globbuf);
2753 #else
2754         /* Get every *-LANG.xml file inside $(ASTDATADIR)/documentation */
2755         if (ast_asprintf(&xmlpattern, "%s/documentation{/thirdparty/,/}*-{%s,%.2s_??,%s}.xml", ast_config_AST_DATA_DIR,
2756                 documentation_language, documentation_language, default_documentation_language) < 0) {
2757                 return 1;
2758         }
2759         globret = glob(xmlpattern, MY_GLOB_FLAGS, NULL, &globbuf);
2760 #endif
2761
2762         ast_debug(3, "gl_pathc %zd\n", globbuf.gl_pathc);
2763         if (globret == GLOB_NOSPACE) {
2764                 ast_log(LOG_WARNING, "XML load failure, glob expansion of pattern '%s' failed: Not enough memory\n", xmlpattern);
2765                 ast_free(xmlpattern);
2766                 return 1;
2767         } else if (globret  == GLOB_ABORTED) {
2768                 ast_log(LOG_WARNING, "XML load failure, glob expansion of pattern '%s' failed: Read error\n", xmlpattern);
2769                 ast_free(xmlpattern);
2770                 return 1;
2771         }
2772         ast_free(xmlpattern);
2773
2774         AST_RWLIST_WRLOCK(&xmldoc_tree);
2775         /* loop over expanded files */
2776         for (i = 0; i < globbuf.gl_pathc; i++) {
2777                 /* check for duplicates (if we already [try to] open the same file. */
2778                 duplicate = 0;
2779                 for (dup = 0; dup < i; dup++) {
2780                         if (!strcmp(globbuf.gl_pathv[i], globbuf.gl_pathv[dup])) {
2781                                 duplicate = 1;
2782                                 break;
2783                         }
2784                 }
2785                 if (duplicate || strchr(globbuf.gl_pathv[i], '*')) {
2786                 /* skip duplicates as well as pathnames not found
2787                  * (due to use of GLOB_NOCHECK in xml_pathmatch) */
2788                         continue;
2789                 }
2790                 tmpdoc = NULL;
2791                 tmpdoc = ast_xml_open(globbuf.gl_pathv[i]);
2792                 if (!tmpdoc) {
2793                         ast_log(LOG_ERROR, "Could not open XML documentation at '%s'\n", globbuf.gl_pathv[i]);
2794                         continue;
2795                 }
2796                 /* Get doc root node and check if it starts with '<docs>' */
2797                 root_node = ast_xml_get_root(tmpdoc);
2798                 if (!root_node) {
2799                         ast_log(LOG_ERROR, "Error getting documentation root node\n");
2800                         ast_xml_close(tmpdoc);
2801                         continue;
2802                 }
2803                 /* Check root node name for malformed xmls. */
2804                 if (strcmp(ast_xml_node_get_name(root_node), "docs")) {
2805                         ast_log(LOG_ERROR, "Documentation file is not well formed!\n");
2806                         ast_xml_close(tmpdoc);
2807                         continue;
2808                 }
2809                 doc_tree = ast_calloc(1, sizeof(*doc_tree));
2810                 if (!doc_tree) {
2811                         ast_log(LOG_ERROR, "Unable to allocate documentation_tree structure!\n");
2812                         ast_xml_close(tmpdoc);
2813                         continue;
2814                 }
2815                 doc_tree->doc = tmpdoc;
2816                 doc_tree->filename = ast_strdup(globbuf.gl_pathv[i]);
2817                 AST_RWLIST_INSERT_TAIL(&xmldoc_tree, doc_tree, entry);
2818         }
2819         AST_RWLIST_UNLOCK(&xmldoc_tree);
2820
2821         globfree(&globbuf);
2822
2823         return 0;
2824 }
2825
2826 #endif /* AST_XML_DOCS */
2827
2828