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