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