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