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