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