Wed Jan 8 2020 09:49:51

Asterisk developer's documentation


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  * \extref libxml2 http://www.xmlsoft.org/
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413586 $")
33 
34 #include "asterisk/_private.h"
35 #include "asterisk/paths.h"
36 #include "asterisk/linkedlists.h"
37 #include "asterisk/strings.h"
38 #include "asterisk/config.h"
39 #include "asterisk/term.h"
40 #include "asterisk/xmldoc.h"
41 
42 #ifdef AST_XML_DOCS
43 
44 /*! \brief Default documentation language. */
45 static const char default_documentation_language[] = "en_US";
46 
47 /*! \brief Number of columns to print when showing the XML documentation with a
48  * 'core show application/function *' CLI command. Used in text wrapping.*/
49 static const int xmldoc_text_columns = 74;
50 
51 /*! \brief This is a value that we will use to let the wrapping mechanism move the cursor
52  * backward and forward xmldoc_max_diff positions before cutting the middle of a
53  * word, trying to find a space or a \n. */
54 static const int xmldoc_max_diff = 5;
55 
56 /*! \brief XML documentation language. */
57 static char documentation_language[6];
58 
59 /*! \brief XML documentation tree */
61  char *filename; /*!< XML document filename. */
62  struct ast_xml_doc *doc; /*!< Open document pointer. */
64 };
65 
66 static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *name, int printname);
67 static int xmldoc_parse_enumlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer);
68 
69 /*!
70  * \brief Container of documentation trees
71  *
72  * \note A RWLIST is a sufficient container type to use here for now.
73  * However, some changes will need to be made to implement ref counting
74  * if reload support is added in the future.
75  */
77 
78 static const struct strcolorized_tags {
79  const char *init; /*!< Replace initial tag with this string. */
80  const char *end; /*!< Replace end tag with this string. */
81  const int colorfg; /*!< Foreground color. */
82  const char *inittag; /*!< Initial tag description. */
83  const char *endtag; /*!< Ending tag description. */
84 } colorized_tags[] = {
85  { "<", ">", COLOR_GREEN, "<replaceable>", "</replaceable>" },
86  { "\'", "\'", COLOR_BLUE, "<literal>", "</literal>" },
87  { "*", "*", COLOR_RED, "<emphasis>", "</emphasis>" },
88  { "\"", "\"", COLOR_YELLOW, "<filename>", "</filename>" },
89  { "\"", "\"", COLOR_CYAN, "<directory>", "</directory>" },
90  { "${", "}", COLOR_GREEN, "<variable>", "</variable>" },
91  { "", "", COLOR_BLUE, "<value>", "</value>" },
92  { "", "", COLOR_BLUE, "<enum>", "</enum>" },
93  { "\'", "\'", COLOR_GRAY, "<astcli>", "</astcli>" },
94 
95  /* Special tags */
96  { "", "", COLOR_YELLOW, "<note>", "</note>" },
97  { "", "", COLOR_RED, "<warning>", "</warning>" }
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 };
108 
109 /*! \internal
110  * \brief Calculate the space in bytes used by a format string
111  * that will be passed to a sprintf function.
112  * \param postbr The format string to use to calculate the length.
113  * \retval The postbr length.
114  */
115 static int xmldoc_postbrlen(const char *postbr)
116 {
117  int postbrreallen = 0, i;
118  size_t postbrlen;
119 
120  if (!postbr) {
121  return 0;
122  }
123  postbrlen = strlen(postbr);
124  for (i = 0; i < postbrlen; i++) {
125  if (postbr[i] == '\t') {
126  postbrreallen += 8 - (postbrreallen % 8);
127  } else {
128  postbrreallen++;
129  }
130  }
131  return postbrreallen;
132 }
133 
134 /*! \internal
135  * \brief Setup postbr to be used while wrapping the text.
136  * Add to postbr array all the spaces and tabs at the beginning of text.
137  * \param postbr output array.
138  * \param len text array length.
139  * \param text Text with format string before the actual string.
140  */
141 static void xmldoc_setpostbr(char *postbr, size_t len, const char *text)
142 {
143  int c, postbrlen = 0;
144 
145  if (!text) {
146  return;
147  }
148 
149  for (c = 0; c < len; c++) {
150  if (text[c] == '\t' || text[c] == ' ') {
151  postbr[postbrlen++] = text[c];
152  } else {
153  break;
154  }
155  }
156  postbr[postbrlen] = '\0';
157 }
158 
159 /*! \internal
160  * \brief Try to find a space or a break in text starting at currentpost
161  * and moving at most maxdiff positions.
162  * Helper for xmldoc_string_wrap().
163  * \param text Input string where it will search.
164  * \param currentpos Current position within text.
165  * \param maxdiff Not move more than maxdiff inside text.
166  * \retval 1 if a space or break is found inside text while moving.
167  * \retval 0 if no space or break is found.
168  */
169 static int xmldoc_wait_nextspace(const char *text, int currentpos, int maxdiff)
170 {
171  int i, textlen;
172 
173  if (!text) {
174  return 0;
175  }
176 
177  textlen = strlen(text);
178  for (i = currentpos; i < textlen; i++) {
179  if (text[i] == ESC) {
180  /* Move to the end of the escape sequence */
181  while (i < textlen && text[i] != 'm') {
182  i++;
183  }
184  } else if (text[i] == ' ' || text[i] == '\n') {
185  /* Found the next space or linefeed */
186  return 1;
187  } else if (i - currentpos > maxdiff) {
188  /* We have looked the max distance and didn't find it */
189  return 0;
190  }
191  }
192 
193  /* Reached the end and did not find it */
194 
195  return 0;
196 }
197 
198 /*! \internal
199  * \brief Helper function for xmldoc_string_wrap().
200  * Try to found a space or a break inside text moving backward
201  * not more than maxdiff positions.
202  * \param text The input string where to search for a space.
203  * \param currentpos The current cursor position.
204  * \param maxdiff The max number of positions to move within text.
205  * \retval 0 If no space is found (Notice that text[currentpos] is not a space or a break)
206  * \retval > 0 If a space or a break is found, and the result is the position relative to
207  * currentpos.
208  */
209 static int xmldoc_foundspace_backward(const char *text, int currentpos, int maxdiff)
210 {
211  int i;
212 
213  for (i = currentpos; i > 0; i--) {
214  if (text[i] == ' ' || text[i] == '\n') {
215  return (currentpos - i);
216  } else if (text[i] == 'm' && (text[i - 1] >= '0' || text[i - 1] <= '9')) {
217  /* give up, we found the end of a possible ESC sequence. */
218  return 0;
219  } else if (currentpos - i > maxdiff) {
220  /* give up, we can't move anymore. */
221  return 0;
222  }
223  }
224 
225  /* we found the beginning of the text */
226 
227  return 0;
228 }
229 
230 /*! \internal
231  * \brief Justify a text to a number of columns.
232  * \param text Input text to be justified.
233  * \param columns Number of columns to preserve in the text.
234  * \param maxdiff Try to not cut a word when goinf down.
235  * \retval NULL on error.
236  * \retval The wrapped text.
237  */
238 static char *xmldoc_string_wrap(const char *text, int columns, int maxdiff)
239 {
240  struct ast_str *tmp;
241  char *ret, postbr[160];
242  int count = 1, i, backspace, needtobreak = 0, colmax, textlen;
243 
244  /* sanity check */
245  if (!text || columns <= 0 || maxdiff < 0) {
246  ast_log(LOG_WARNING, "Passing wrong arguments while trying to wrap the text\n");
247  return NULL;
248  }
249 
250  tmp = ast_str_create(strlen(text) * 3);
251 
252  if (!tmp) {
253  return NULL;
254  }
255 
256  /* Check for blanks and tabs and put them in postbr. */
257  xmldoc_setpostbr(postbr, sizeof(postbr), text);
258  colmax = columns - xmldoc_postbrlen(postbr);
259 
260  textlen = strlen(text);
261  for (i = 0; i < textlen; i++) {
262  if (needtobreak || !(count % colmax)) {
263  if (text[i] == ' ') {
264  ast_str_append(&tmp, 0, "\n%s", postbr);
265  needtobreak = 0;
266  count = 1;
267  } else if (text[i] != '\n') {
268  needtobreak = 1;
269  if (xmldoc_wait_nextspace(text, i, maxdiff)) {
270  /* wait for the next space */
271  ast_str_append(&tmp, 0, "%c", text[i]);
272  continue;
273  }
274  /* Try to look backwards */
275  backspace = xmldoc_foundspace_backward(text, i, maxdiff);
276  if (backspace) {
277  needtobreak = 1;
278  ast_str_truncate(tmp, -backspace);
279  i -= backspace + 1;
280  continue;
281  }
282  ast_str_append(&tmp, 0, "\n%s", postbr);
283  needtobreak = 0;
284  count = 1;
285  }
286  /* skip blanks after a \n */
287  while (text[i] == ' ') {
288  i++;
289  }
290  }
291  if (text[i] == '\n') {
292  xmldoc_setpostbr(postbr, sizeof(postbr), &text[i] + 1);
293  colmax = columns - xmldoc_postbrlen(postbr);
294  needtobreak = 0;
295  count = 1;
296  }
297  if (text[i] == ESC) {
298  /* Ignore Escape sequences. */
299  do {
300  ast_str_append(&tmp, 0, "%c", text[i]);
301  i++;
302  } while (i < textlen && text[i] != 'm');
303  } else {
304  count++;
305  }
306  ast_str_append(&tmp, 0, "%c", text[i]);
307  }
308 
309  ret = ast_strdup(ast_str_buffer(tmp));
310  ast_free(tmp);
311 
312  return ret;
313 }
314 
315 char *ast_xmldoc_printable(const char *bwinput, int withcolors)
316 {
317  struct ast_str *colorized;
318  char *wrapped = NULL;
319  int i, c, len, colorsection;
320  char *tmp;
321  size_t bwinputlen;
322  static const int base_fg = COLOR_CYAN;
323 
324  if (!bwinput) {
325  return NULL;
326  }
327 
328  bwinputlen = strlen(bwinput);
329 
330  if (!(colorized = ast_str_create(256))) {
331  return NULL;
332  }
333 
334  if (withcolors) {
335  ast_term_color_code(&colorized, base_fg, 0);
336  if (!colorized) {
337  return NULL;
338  }
339  }
340 
341  for (i = 0; i < bwinputlen; i++) {
342  colorsection = 0;
343  /* Check if we are at the beginning of a tag to be colorized. */
344  for (c = 0; c < ARRAY_LEN(colorized_tags); c++) {
345  if (strncasecmp(bwinput + i, colorized_tags[c].inittag, strlen(colorized_tags[c].inittag))) {
346  continue;
347  }
348 
349  if (!(tmp = strcasestr(bwinput + i + strlen(colorized_tags[c].inittag), colorized_tags[c].endtag))) {
350  continue;
351  }
352 
353  len = tmp - (bwinput + i + strlen(colorized_tags[c].inittag));
354 
355  /* Setup color */
356  if (withcolors) {
358  /* Turn off *bright* colors */
359  ast_term_color_code(&colorized, colorized_tags[c].colorfg & 0x7f, 0);
360  } else {
361  /* Turn on *bright* colors */
362  ast_term_color_code(&colorized, colorized_tags[c].colorfg | 0x80, 0);
363  }
364  if (!colorized) {
365  return NULL;
366  }
367  }
368 
369  /* copy initial string replace */
370  ast_str_append(&colorized, 0, "%s", colorized_tags[c].init);
371  if (!colorized) {
372  return NULL;
373  }
374  {
375  char buf[len + 1];
376  ast_copy_string(buf, bwinput + i + strlen(colorized_tags[c].inittag), sizeof(buf));
377  ast_str_append(&colorized, 0, "%s", buf);
378  }
379  if (!colorized) {
380  return NULL;
381  }
382 
383  /* copy the ending string replace */
384  ast_str_append(&colorized, 0, "%s", colorized_tags[c].end);
385  if (!colorized) {
386  return NULL;
387  }
388 
389  /* Continue with the last color. */
390  if (withcolors) {
391  ast_term_color_code(&colorized, base_fg, 0);
392  if (!colorized) {
393  return NULL;
394  }
395  }
396 
397  i += len + strlen(colorized_tags[c].endtag) + strlen(colorized_tags[c].inittag) - 1;
398  colorsection = 1;
399  break;
400  }
401 
402  if (!colorsection) {
403  ast_str_append(&colorized, 0, "%c", bwinput[i]);
404  if (!colorized) {
405  return NULL;
406  }
407  }
408  }
409 
410  if (withcolors) {
411  ast_str_append(&colorized, 0, "%s", term_end());
412  if (!colorized) {
413  return NULL;
414  }
415  }
416 
417  /* Wrap the text, notice that string wrap will avoid cutting an ESC sequence. */
418  wrapped = xmldoc_string_wrap(ast_str_buffer(colorized), xmldoc_text_columns, xmldoc_max_diff);
419 
420  ast_free(colorized);
421 
422  return wrapped;
423 }
424 
425 /*! \internal
426  * \brief Cleanup spaces and tabs after a \n
427  * \param text String to be cleaned up.
428  * \param output buffer (not already allocated).
429  * \param lastspaces Remove last spaces in the string.
430  */
431 static void xmldoc_string_cleanup(const char *text, struct ast_str **output, int lastspaces)
432 {
433  int i;
434  size_t textlen;
435 
436  if (!text) {
437  *output = NULL;
438  return;
439  }
440 
441  textlen = strlen(text);
442 
443  *output = ast_str_create(textlen);
444  if (!(*output)) {
445  ast_log(LOG_ERROR, "Problem allocating output buffer\n");
446  return;
447  }
448 
449  for (i = 0; i < textlen; i++) {
450  if (text[i] == '\n' || text[i] == '\r') {
451  /* remove spaces/tabs/\n after a \n. */
452  while (text[i + 1] == '\t' || text[i + 1] == '\r' || text[i + 1] == '\n') {
453  i++;
454  }
455  ast_str_append(output, 0, " ");
456  continue;
457  } else {
458  ast_str_append(output, 0, "%c", text[i]);
459  }
460  }
461 
462  /* remove last spaces (we don't want always to remove the trailing spaces). */
463  if (lastspaces) {
464  ast_str_trim_blanks(*output);
465  }
466 }
467 
468 /*! \internal
469  * \brief Check if the given attribute on the given node matches the given value.
470  * \param node the node to match
471  * \param attr the name of the attribute
472  * \param value the expected value of the attribute
473  * \retval true if the given attribute contains the given value
474  * \retval false if the given attribute does not exist or does not contain the given value
475  */
476 static int xmldoc_attribute_match(struct ast_xml_node *node, const char *attr, const char *value)
477 {
478  const char *attr_value = ast_xml_get_attribute(node, attr);
479  int match = attr_value && !strcmp(attr_value, value);
480  ast_xml_free_attr(attr_value);
481  return match;
482 }
483 
484 /*! \internal
485  * \brief Get the application/function node for 'name' application/function with language 'language'
486  * and module 'module' if we don't find any, get the first application
487  * with 'name' no matter which language or module.
488  * \param type 'application', 'function', ...
489  * \param name Application or Function name.
490  * \param module Module item is in.
491  * \param language Try to get this language (if not found try with en_US)
492  * \retval NULL on error.
493  * \retval A node of type ast_xml_node.
494  */
495 static struct ast_xml_node *xmldoc_get_node(const char *type, const char *name, const char *module, const char *language)
496 {
497  struct ast_xml_node *node = NULL;
498  struct ast_xml_node *first_match = NULL;
499  struct ast_xml_node *lang_match = NULL;
500  struct documentation_tree *doctree;
501 
503  AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
504  /* the core xml documents have priority over thirdparty document. */
505  node = ast_xml_get_root(doctree->doc);
506  if (!node) {
507  break;
508  }
509 
510  node = ast_xml_node_get_children(node);
511  while ((node = ast_xml_find_element(node, type, "name", name))) {
512  if (!ast_xml_node_get_children(node)) {
513  /* ignore empty nodes */
514  node = ast_xml_node_get_next(node);
515  continue;
516  }
517 
518  if (!first_match) {
519  first_match = node;
520  }
521 
522  /* Check language */
523  if (xmldoc_attribute_match(node, "language", language)) {
524  if (!lang_match) {
525  lang_match = node;
526  }
527 
528  /* if module is empty we have a match */
529  if (ast_strlen_zero(module)) {
530  break;
531  }
532 
533  /* Check module */
534  if (xmldoc_attribute_match(node, "module", module)) {
535  break;
536  }
537  }
538 
539  node = ast_xml_node_get_next(node);
540  }
541 
542  /* if we matched lang and module return this match */
543  if (node) {
544  break;
545  }
546 
547  /* we didn't match lang and module, just return the first
548  * result with a matching language if we have one */
549  if (lang_match) {
550  node = lang_match;
551  break;
552  }
553 
554  /* we didn't match with only the language, just return the
555  * first match */
556  if (first_match) {
557  node = first_match;
558  break;
559  }
560  }
562 
563  return node;
564 }
565 
566 /*! \internal
567  * \brief Helper function used to build the syntax, it allocates the needed buffer (or reallocates it),
568  * and based on the reverse value it makes use of fmt to print the parameter list inside the
569  * realloced buffer (syntax).
570  * \param reverse We are going backwards while generating the syntax?
571  * \param len Current length of 'syntax' buffer.
572  * \param syntax Output buffer for the concatenated values.
573  * \param fmt A format string that will be used in a sprintf call.
574  */
575 static void __attribute__((format(printf, 4, 5))) xmldoc_reverse_helper(int reverse, int *len, char **syntax, const char *fmt, ...)
576 {
577  int totlen;
578  int tmpfmtlen;
579  char *tmpfmt;
580  char *new_syntax;
581  char tmp;
582  va_list ap;
583 
584  va_start(ap, fmt);
585  if (ast_vasprintf(&tmpfmt, fmt, ap) < 0) {
586  va_end(ap);
587  return;
588  }
589  va_end(ap);
590 
591  tmpfmtlen = strlen(tmpfmt);
592  totlen = *len + tmpfmtlen + 1;
593 
594  new_syntax = ast_realloc(*syntax, totlen);
595  if (!new_syntax) {
596  ast_free(tmpfmt);
597  return;
598  }
599  *syntax = new_syntax;
600 
601  if (reverse) {
602  memmove(*syntax + tmpfmtlen, *syntax, *len);
603  /* Save this char, it will be overwritten by the \0 of strcpy. */
604  tmp = (*syntax)[0];
605  strcpy(*syntax, tmpfmt);
606  /* Restore the already saved char. */
607  (*syntax)[tmpfmtlen] = tmp;
608  (*syntax)[totlen - 1] = '\0';
609  } else {
610  strcpy(*syntax + *len, tmpfmt);
611  }
612 
613  *len = totlen - 1;
614  ast_free(tmpfmt);
615 }
616 
617 /*! \internal
618  * \brief Check if the passed node has 'what' tags inside it.
619  * \param node Root node to search 'what' elements.
620  * \param what node name to search inside node.
621  * \retval 1 If a 'what' element is found inside 'node'.
622  * \retval 0 If no 'what' is found inside 'node'.
623  */
624 static int xmldoc_has_inside(struct ast_xml_node *fixnode, const char *what)
625 {
626  struct ast_xml_node *node = fixnode;
627 
628  for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
629  if (!strcasecmp(ast_xml_node_get_name(node), what)) {
630  return 1;
631  }
632  }
633  return 0;
634 }
635 
636 /*! \internal
637  * \brief Check if the passed node has at least one node inside it.
638  * \param node Root node to search node elements.
639  * \retval 1 If a node element is found inside 'node'.
640  * \retval 0 If no node is found inside 'node'.
641  */
642 static int xmldoc_has_nodes(struct ast_xml_node *fixnode)
643 {
644  struct ast_xml_node *node = fixnode;
645 
646  for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
647  if (strcasecmp(ast_xml_node_get_name(node), "text")) {
648  return 1;
649  }
650  }
651  return 0;
652 }
653 
654 /*! \internal
655  * \brief Check if the passed node has at least one specialtag.
656  * \param node Root node to search "specialtags" elements.
657  * \retval 1 If a "specialtag" element is found inside 'node'.
658  * \retval 0 If no "specialtag" is found inside 'node'.
659  */
660 static int xmldoc_has_specialtags(struct ast_xml_node *fixnode)
661 {
662  struct ast_xml_node *node = fixnode;
663  int i;
664 
665  for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
666  for (i = 0; i < ARRAY_LEN(special_tags); i++) {
667  if (!strcasecmp(ast_xml_node_get_name(node), special_tags[i].tagname)) {
668  return 1;
669  }
670  }
671  }
672  return 0;
673 }
674 
675 /*! \internal
676  * \brief Build the syntax for a specified starting node.
677  * \param rootnode A pointer to the ast_xml root node.
678  * \param rootname Name of the application, function, option, etc. to build the syntax.
679  * \param childname The name of each parameter node.
680  * \param printparenthesis Boolean if we must print parenthesis if not parameters are found in the rootnode.
681  * \param printrootname Boolean if we must print the rootname before the syntax and parenthesis at the begining/end.
682  * \retval NULL on error.
683  * \retval An ast_malloc'ed string with the syntax generated.
684  */
685 static char *xmldoc_get_syntax_fun(struct ast_xml_node *rootnode, const char *rootname, const char *childname, int printparenthesis, int printrootname)
686 {
687 #define GOTONEXT(__rev, __a) (__rev ? ast_xml_node_get_prev(__a) : ast_xml_node_get_next(__a))
688 #define ISLAST(__rev, __a) (__rev == 1 ? (ast_xml_node_get_prev(__a) ? 0 : 1) : (ast_xml_node_get_next(__a) ? 0 : 1))
689 #define MP(__a) ((multiple ? __a : ""))
690  struct ast_xml_node *node = NULL, *firstparam = NULL, *lastparam = NULL;
691  const char *paramtype, *multipletype, *paramnameattr, *attrargsep, *parenthesis, *argname;
692  int reverse, required, paramcount = 0, openbrackets = 0, len = 0, hasparams=0;
693  int reqfinode = 0, reqlanode = 0, optmidnode = 0, prnparenthesis, multiple;
694  char *syntax = NULL, *argsep, *paramname;
695 
696  if (ast_strlen_zero(rootname) || ast_strlen_zero(childname)) {
697  ast_log(LOG_WARNING, "Tried to look in XML tree with faulty rootname or childname while creating a syntax.\n");
698  return NULL;
699  }
700 
701  if (!rootnode || !ast_xml_node_get_children(rootnode)) {
702  /* If the rootnode field is not found, at least print name. */
703  if (ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : "")) < 0) {
704  syntax = NULL;
705  }
706  return syntax;
707  }
708 
709  /* Get the argument separator from the root node attribute name 'argsep', if not found
710  defaults to ','. */
711  attrargsep = ast_xml_get_attribute(rootnode, "argsep");
712  if (attrargsep) {
713  argsep = ast_strdupa(attrargsep);
714  ast_xml_free_attr(attrargsep);
715  } else {
716  argsep = ast_strdupa(",");
717  }
718 
719  /* Get order of evaluation. */
720  for (node = ast_xml_node_get_children(rootnode); node; node = ast_xml_node_get_next(node)) {
721  if (strcasecmp(ast_xml_node_get_name(node), childname)) {
722  continue;
723  }
724  required = 0;
725  hasparams = 1;
726  if ((paramtype = ast_xml_get_attribute(node, "required"))) {
727  if (ast_true(paramtype)) {
728  required = 1;
729  }
730  ast_xml_free_attr(paramtype);
731  }
732 
733  lastparam = node;
734  reqlanode = required;
735 
736  if (!firstparam) {
737  /* first parameter node */
738  firstparam = node;
739  reqfinode = required;
740  }
741  }
742 
743  if (!hasparams) {
744  /* This application, function, option, etc, doesn't have any params. */
745  if (ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : "")) < 0) {
746  syntax = NULL;
747  }
748  return syntax;
749  }
750 
751  if (reqfinode && reqlanode) {
752  /* check midnode */
753  for (node = ast_xml_node_get_children(rootnode); node; node = ast_xml_node_get_next(node)) {
754  if (strcasecmp(ast_xml_node_get_name(node), childname)) {
755  continue;
756  }
757  if (node != firstparam && node != lastparam) {
758  if ((paramtype = ast_xml_get_attribute(node, "required"))) {
759  if (!ast_true(paramtype)) {
760  optmidnode = 1;
761  ast_xml_free_attr(paramtype);
762  break;
763  }
764  ast_xml_free_attr(paramtype);
765  }
766  }
767  }
768  }
769 
770  if ((!reqfinode && reqlanode) || (reqfinode && reqlanode && optmidnode)) {
771  reverse = 1;
772  node = lastparam;
773  } else {
774  reverse = 0;
775  node = firstparam;
776  }
777 
778  /* init syntax string. */
779  if (reverse) {
780  xmldoc_reverse_helper(reverse, &len, &syntax,
781  (printrootname ? (printrootname == 2 ? ")]" : ")"): ""));
782  } else {
783  xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", (printrootname ? rootname : ""),
784  (printrootname ? (printrootname == 2 ? "[(" : "(") : ""));
785  }
786 
787  for (; node; node = GOTONEXT(reverse, node)) {
788  if (strcasecmp(ast_xml_node_get_name(node), childname)) {
789  continue;
790  }
791 
792  /* Get the argument name, if it is not the leaf, go inside that parameter. */
793  if (xmldoc_has_inside(node, "argument")) {
794  parenthesis = ast_xml_get_attribute(node, "hasparams");
795  prnparenthesis = 0;
796  if (parenthesis) {
797  prnparenthesis = ast_true(parenthesis);
798  if (!strcasecmp(parenthesis, "optional")) {
799  prnparenthesis = 2;
800  }
801  ast_xml_free_attr(parenthesis);
802  }
803  argname = ast_xml_get_attribute(node, "name");
804  if (argname) {
805  paramname = xmldoc_get_syntax_fun(node, argname, "argument", prnparenthesis, prnparenthesis);
806  ast_xml_free_attr(argname);
807  } else {
808  /* Malformed XML, print **UNKOWN** */
809  paramname = ast_strdup("**unknown**");
810  }
811  } else {
812  paramnameattr = ast_xml_get_attribute(node, "name");
813  if (!paramnameattr) {
814  ast_log(LOG_WARNING, "Malformed XML %s: no %s name\n", rootname, childname);
815  if (syntax) {
816  /* Free already allocated syntax */
817  ast_free(syntax);
818  }
819  /* to give up is ok? */
820  if (ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : "")) < 0) {
821  syntax = NULL;
822  }
823  return syntax;
824  }
825  paramname = ast_strdup(paramnameattr);
826  ast_xml_free_attr(paramnameattr);
827  }
828 
829  if (!paramname) {
830  return NULL;
831  }
832 
833  /* Defaults to 'false'. */
834  multiple = 0;
835  if ((multipletype = ast_xml_get_attribute(node, "multiple"))) {
836  if (ast_true(multipletype)) {
837  multiple = 1;
838  }
839  ast_xml_free_attr(multipletype);
840  }
841 
842  required = 0; /* Defaults to 'false'. */
843  if ((paramtype = ast_xml_get_attribute(node, "required"))) {
844  if (ast_true(paramtype)) {
845  required = 1;
846  }
847  ast_xml_free_attr(paramtype);
848  }
849 
850  /* build syntax core. */
851 
852  if (required) {
853  /* First parameter */
854  if (!paramcount) {
855  xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s%s", paramname, MP("["), MP(argsep), MP("...]"));
856  } else {
857  /* Time to close open brackets. */
858  while (openbrackets > 0) {
859  xmldoc_reverse_helper(reverse, &len, &syntax, (reverse ? "[" : "]"));
860  openbrackets--;
861  }
862  if (reverse) {
863  xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", paramname, argsep);
864  } else {
865  xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", argsep, paramname);
866  }
867  xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s", MP("["), MP(argsep), MP("...]"));
868  }
869  } else {
870  /* First parameter */
871  if (!paramcount) {
872  xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s]", paramname, MP("["), MP(argsep), MP("...]"));
873  } else {
874  if (ISLAST(reverse, node)) {
875  /* This is the last parameter. */
876  if (reverse) {
877  xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s]%s", paramname,
878  MP("["), MP(argsep), MP("...]"), argsep);
879  } else {
880  xmldoc_reverse_helper(reverse, &len, &syntax, "%s[%s%s%s%s]", argsep, paramname,
881  MP("["), MP(argsep), MP("...]"));
882  }
883  } else {
884  if (reverse) {
885  xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s%s%s]", paramname, argsep,
886  MP("["), MP(argsep), MP("...]"));
887  } else {
888  xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s%s", argsep, paramname,
889  MP("["), MP(argsep), MP("...]"));
890  }
891  openbrackets++;
892  }
893  }
894  }
895  ast_free(paramname);
896 
897  paramcount++;
898  }
899 
900  /* Time to close open brackets. */
901  while (openbrackets > 0) {
902  xmldoc_reverse_helper(reverse, &len, &syntax, (reverse ? "[" : "]"));
903  openbrackets--;
904  }
905 
906  /* close syntax string. */
907  if (reverse) {
908  xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", (printrootname ? rootname : ""),
909  (printrootname ? (printrootname == 2 ? "[(" : "(") : ""));
910  } else {
911  xmldoc_reverse_helper(reverse, &len, &syntax, (printrootname ? (printrootname == 2 ? ")]" : ")") : ""));
912  }
913 
914  return syntax;
915 #undef ISLAST
916 #undef GOTONEXT
917 #undef MP
918 }
919 
920 /*! \internal
921  * \brief Parse an enumlist inside a <parameter> to generate a COMMAND
922  * syntax.
923  * \param fixnode A pointer to the <enumlist> node.
924  * \retval {<unknown>} on error.
925  * \retval A string inside brackets {} with the enum's separated by pipes |.
926  */
927 static char *xmldoc_parse_cmd_enumlist(struct ast_xml_node *fixnode)
928 {
929  struct ast_xml_node *node = fixnode;
930  struct ast_str *paramname;
931  char *enumname, *ret;
932  int first = 1;
933 
934  paramname = ast_str_create(128);
935  if (!paramname) {
936  return ast_strdup("{<unkown>}");
937  }
938 
939  ast_str_append(&paramname, 0, "{");
940 
941  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
942  if (strcasecmp(ast_xml_node_get_name(node), "enum")) {
943  continue;
944  }
945 
946  enumname = xmldoc_get_syntax_cmd(node, "", 0);
947  if (!enumname) {
948  continue;
949  }
950  if (!first) {
951  ast_str_append(&paramname, 0, "|");
952  }
953  ast_str_append(&paramname, 0, "%s", enumname);
954  first = 0;
955  ast_free(enumname);
956  }
957 
958  ast_str_append(&paramname, 0, "}");
959 
960  ret = ast_strdup(ast_str_buffer(paramname));
961  ast_free(paramname);
962 
963  return ret;
964 }
965 
966 /*! \internal
967  * \brief Generate a syntax of COMMAND type.
968  * \param fixnode The <syntax> node pointer.
969  * \param name The name of the 'command'.
970  * \param printname Print the name of the command before the paramters?
971  * \retval On error, return just 'name'.
972  * \retval On success return the generated syntax.
973  */
974 static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *name, int printname)
975 {
976  struct ast_str *syntax;
977  struct ast_xml_node *tmpnode, *node = fixnode;
978  char *ret, *paramname;
979  const char *paramtype, *attrname, *literal;
980  int required, isenum, first = 1, isliteral;
981 
982  syntax = ast_str_create(128);
983  if (!syntax) {
984  /* at least try to return something... */
985  return ast_strdup(name);
986  }
987 
988  /* append name to output string. */
989  if (printname) {
990  ast_str_append(&syntax, 0, "%s", name);
991  first = 0;
992  }
993 
994  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
995  if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
996  continue;
997  }
998 
999  if (xmldoc_has_inside(node, "parameter")) {
1000  /* is this a recursive parameter. */
1001  paramname = xmldoc_get_syntax_cmd(node, "", 0);
1002  isenum = 1;
1003  } else {
1004  for (tmpnode = ast_xml_node_get_children(node); tmpnode; tmpnode = ast_xml_node_get_next(tmpnode)) {
1005  if (!strcasecmp(ast_xml_node_get_name(tmpnode), "enumlist")) {
1006  break;
1007  }
1008  }
1009  if (tmpnode) {
1010  /* parse enumlist (note that this is a special enumlist
1011  that is used to describe a syntax like {<param1>|<param2>|...} */
1012  paramname = xmldoc_parse_cmd_enumlist(tmpnode);
1013  isenum = 1;
1014  } else {
1015  /* this is a simple parameter. */
1016  attrname = ast_xml_get_attribute(node, "name");
1017  if (!attrname) {
1018  /* ignore this bogus parameter and continue. */
1019  continue;
1020  }
1021  paramname = ast_strdup(attrname);
1022  ast_xml_free_attr(attrname);
1023  isenum = 0;
1024  }
1025  }
1026 
1027  /* Is this parameter required? */
1028  required = 0;
1029  paramtype = ast_xml_get_attribute(node, "required");
1030  if (paramtype) {
1031  required = ast_true(paramtype);
1032  ast_xml_free_attr(paramtype);
1033  }
1034 
1035  /* Is this a replaceable value or a fixed parameter value? */
1036  isliteral = 0;
1037  literal = ast_xml_get_attribute(node, "literal");
1038  if (literal) {
1039  isliteral = ast_true(literal);
1040  ast_xml_free_attr(literal);
1041  }
1042 
1043  /* if required="false" print with [...].
1044  * if literal="true" or is enum print without <..>.
1045  * if not first print a space at the beginning.
1046  */
1047  ast_str_append(&syntax, 0, "%s%s%s%s%s%s",
1048  (first ? "" : " "),
1049  (required ? "" : "["),
1050  (isenum || isliteral ? "" : "<"),
1051  paramname,
1052  (isenum || isliteral ? "" : ">"),
1053  (required ? "" : "]"));
1054  first = 0;
1055  ast_free(paramname);
1056  }
1057 
1058  /* return a common string. */
1059  ret = ast_strdup(ast_str_buffer(syntax));
1060  ast_free(syntax);
1061 
1062  return ret;
1063 }
1064 
1065 /*! \internal
1066  * \brief Generate an AMI action syntax.
1067  * \param fixnode The manager action node pointer.
1068  * \param name The name of the manager action.
1069  * \retval The generated syntax.
1070  * \retval NULL on error.
1071  */
1072 static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char *name)
1073 {
1074  struct ast_str *syntax;
1075  struct ast_xml_node *node = fixnode;
1076  const char *paramtype, *attrname;
1077  int required;
1078  char *ret;
1079 
1080  syntax = ast_str_create(128);
1081  if (!syntax) {
1082  return ast_strdup(name);
1083  }
1084 
1085  ast_str_append(&syntax, 0, "Action: %s", name);
1086 
1087  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1088  if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
1089  continue;
1090  }
1091 
1092  /* Is this parameter required? */
1093  required = 0;
1094  paramtype = ast_xml_get_attribute(node, "required");
1095  if (paramtype) {
1096  required = ast_true(paramtype);
1097  ast_xml_free_attr(paramtype);
1098  }
1099 
1100  attrname = ast_xml_get_attribute(node, "name");
1101  if (!attrname) {
1102  /* ignore this bogus parameter and continue. */
1103  continue;
1104  }
1105 
1106  ast_str_append(&syntax, 0, "\n%s%s:%s <value>",
1107  (required ? "" : "["),
1108  attrname,
1109  (required ? "" : "]"));
1110 
1111  ast_xml_free_attr(attrname);
1112  }
1113 
1114  /* return a common string. */
1115  ret = ast_strdup(ast_str_buffer(syntax));
1116  ast_free(syntax);
1117 
1118  return ret;
1119 }
1120 
1121 /*! \brief Types of syntax that we are able to generate. */
1126 };
1127 
1128 /*! \brief Mapping between type of node and type of syntax to generate. */
1129 static struct strsyntaxtype {
1130  const char *type;
1132 } stxtype[] = {
1133  { "function", FUNCTION_SYNTAX },
1134  { "application", FUNCTION_SYNTAX },
1135  { "manager", MANAGER_SYNTAX },
1136  { "agi", COMMAND_SYNTAX }
1137 };
1138 
1139 /*! \internal
1140  * \brief Get syntax type based on type of node.
1141  * \param type Type of node.
1142  * \retval The type of syntax to generate based on the type of node.
1143  */
1144 static enum syntaxtype xmldoc_get_syntax_type(const char *type)
1145 {
1146  int i;
1147  for (i=0; i < ARRAY_LEN(stxtype); i++) {
1148  if (!strcasecmp(stxtype[i].type, type)) {
1149  return stxtype[i].stxtype;
1150  }
1151  }
1152 
1153  return FUNCTION_SYNTAX;
1154 }
1155 
1156 char *ast_xmldoc_build_syntax(const char *type, const char *name, const char *module)
1157 {
1158  struct ast_xml_node *node;
1159  char *syntax = NULL;
1160 
1161  node = xmldoc_get_node(type, name, module, documentation_language);
1162  if (!node) {
1163  return NULL;
1164  }
1165 
1166  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1167  if (!strcasecmp(ast_xml_node_get_name(node), "syntax")) {
1168  break;
1169  }
1170  }
1171 
1172  if (node) {
1173  switch (xmldoc_get_syntax_type(type)) {
1174  case FUNCTION_SYNTAX:
1175  syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
1176  break;
1177  case COMMAND_SYNTAX:
1178  syntax = xmldoc_get_syntax_cmd(node, name, 1);
1179  break;
1180  case MANAGER_SYNTAX:
1181  syntax = xmldoc_get_syntax_manager(node, name);
1182  break;
1183  default:
1184  syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
1185  }
1186  }
1187  return syntax;
1188 }
1189 
1190 /*! \internal
1191  * \brief Parse a <para> element.
1192  * \param node The <para> element pointer.
1193  * \param tabs Added this string before the content of the <para> element.
1194  * \param posttabs Added this string after the content of the <para> element.
1195  * \param buffer This must be an already allocated ast_str. It will be used
1196  * to store the result (if already has something it will be appended to the current
1197  * string).
1198  * \retval 1 If 'node' is a named 'para'.
1199  * \retval 2 If data is appended in buffer.
1200  * \retval 0 on error.
1201  */
1202 static int xmldoc_parse_para(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer)
1203 {
1204  const char *tmptext;
1205  struct ast_xml_node *tmp;
1206  int ret = 0;
1207  struct ast_str *tmpstr;
1208 
1209  if (!node || !ast_xml_node_get_children(node)) {
1210  return ret;
1211  }
1212 
1213  if (strcasecmp(ast_xml_node_get_name(node), "para")) {
1214  return ret;
1215  }
1216 
1217  ast_str_append(buffer, 0, "%s", tabs);
1218 
1219  ret = 1;
1220 
1221  for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
1222  /* Get the text inside the <para> element and append it to buffer. */
1223  tmptext = ast_xml_get_text(tmp);
1224  if (tmptext) {
1225  /* Strip \n etc. */
1226  xmldoc_string_cleanup(tmptext, &tmpstr, 0);
1227  ast_xml_free_text(tmptext);
1228  if (tmpstr) {
1229  if (strcasecmp(ast_xml_node_get_name(tmp), "text")) {
1230  ast_str_append(buffer, 0, "<%s>%s</%s>", ast_xml_node_get_name(tmp),
1231  ast_str_buffer(tmpstr), ast_xml_node_get_name(tmp));
1232  } else {
1233  ast_str_append(buffer, 0, "%s", ast_str_buffer(tmpstr));
1234  }
1235  ast_free(tmpstr);
1236  ret = 2;
1237  }
1238  }
1239  }
1240 
1241  ast_str_append(buffer, 0, "%s", posttabs);
1242 
1243  return ret;
1244 }
1245 
1246 /*! \internal
1247  * \brief Parse special elements defined in 'struct special_tags' special elements must have a <para> element inside them.
1248  * \param fixnode special tag node pointer.
1249  * \param tabs put tabs before printing the node content.
1250  * \param posttabs put posttabs after printing node content.
1251  * \param buffer Output buffer, the special tags will be appended here.
1252  * \retval 0 if no special element is parsed.
1253  * \retval 1 if a special element is parsed (data is appended to buffer).
1254  * \retval 2 if a special element is parsed and also a <para> element is parsed inside the specialtag.
1255  */
1256 static int xmldoc_parse_specialtags(struct ast_xml_node *fixnode, const char *tabs, const char *posttabs, struct ast_str **buffer)
1257 {
1258  struct ast_xml_node *node = fixnode;
1259  int ret = 0, i, count = 0;
1260 
1261  if (!node || !ast_xml_node_get_children(node)) {
1262  return ret;
1263  }
1264 
1265  for (i = 0; i < ARRAY_LEN(special_tags); i++) {
1266  if (strcasecmp(ast_xml_node_get_name(node), special_tags[i].tagname)) {
1267  continue;
1268  }
1269 
1270  ret = 1;
1271  /* This is a special tag. */
1272 
1273  /* concat data */
1274  if (!ast_strlen_zero(special_tags[i].init)) {
1275  ast_str_append(buffer, 0, "%s%s", tabs, special_tags[i].init);
1276  }
1277 
1278  /* parse <para> elements inside special tags. */
1279  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1280  /* first <para> just print it without tabs at the begining. */
1281  if (xmldoc_parse_para(node, (!count ? "" : tabs), posttabs, buffer) == 2) {
1282  ret = 2;
1283  }
1284  }
1285 
1286  if (!ast_strlen_zero(special_tags[i].end)) {
1287  ast_str_append(buffer, 0, "%s%s", special_tags[i].end, posttabs);
1288  }
1289 
1290  break;
1291  }
1292 
1293  return ret;
1294 }
1295 
1296 /*! \internal
1297  * \brief Parse an <argument> element from the xml documentation.
1298  * \param fixnode Pointer to the 'argument' xml node.
1299  * \param insideparameter If we are parsing an <argument> inside a <parameter>.
1300  * \param paramtabs pre tabs if we are inside a parameter element.
1301  * \param tabs What to be printed before the argument name.
1302  * \param buffer Output buffer to put values found inside the <argument> element.
1303  * \retval 1 If there is content inside the argument.
1304  * \retval 0 If the argument element is not parsed, or there is no content inside it.
1305  */
1306 static int xmldoc_parse_argument(struct ast_xml_node *fixnode, int insideparameter, const char *paramtabs, const char *tabs, struct ast_str **buffer)
1307 {
1308  struct ast_xml_node *node = fixnode;
1309  const char *argname;
1310  int count = 0, ret = 0;
1311 
1312  if (!node || !ast_xml_node_get_children(node)) {
1313  return ret;
1314  }
1315 
1316  /* Print the argument names */
1317  argname = ast_xml_get_attribute(node, "name");
1318  if (!argname) {
1319  return 0;
1320  }
1321  if (xmldoc_has_inside(node, "para") || xmldoc_has_specialtags(node)) {
1322  ast_str_append(buffer, 0, "%s%s%s", tabs, argname, (insideparameter ? "\n" : ""));
1323  ast_xml_free_attr(argname);
1324  } else {
1325  ast_xml_free_attr(argname);
1326  return 0;
1327  }
1328 
1329  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1330  if (xmldoc_parse_para(node, (insideparameter ? paramtabs : (!count ? " - " : tabs)), "\n", buffer) == 2) {
1331  count++;
1332  ret = 1;
1333  } else if (xmldoc_parse_specialtags(node, (insideparameter ? paramtabs : (!count ? " - " : tabs)), "\n", buffer) == 2) {
1334  count++;
1335  ret = 1;
1336  }
1337  }
1338 
1339  return ret;
1340 }
1341 
1342 /*! \internal
1343  * \brief Parse a <variable> node inside a <variablelist> node.
1344  * \param node The variable node to parse.
1345  * \param tabs A string to be appended at the begining of the output that will be stored
1346  * in buffer.
1347  * \param buffer This must be an already created ast_str. It will be used
1348  * to store the result (if already has something it will be appended to the current
1349  * string).
1350  * \retval 0 if no data is appended.
1351  * \retval 1 if data is appended.
1352  */
1353 static int xmldoc_parse_variable(struct ast_xml_node *node, const char *tabs, struct ast_str **buffer)
1354 {
1355  struct ast_xml_node *tmp;
1356  const char *valname;
1357  const char *tmptext;
1358  struct ast_str *cleanstr;
1359  int ret = 0, printedpara=0;
1360 
1361  for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
1362  if (xmldoc_parse_para(tmp, (ret ? tabs : ""), "\n", buffer)) {
1363  printedpara = 1;
1364  continue;
1365  } else if (xmldoc_parse_specialtags(tmp, (ret ? tabs : ""), "\n", buffer)) {
1366  printedpara = 1;
1367  continue;
1368  }
1369 
1370  if (strcasecmp(ast_xml_node_get_name(tmp), "value")) {
1371  continue;
1372  }
1373 
1374  /* Parse a <value> tag only. */
1375  if (!printedpara) {
1376  ast_str_append(buffer, 0, "\n");
1377  printedpara = 1;
1378  }
1379  /* Parse each <value name='valuename'>desciption</value> */
1380  valname = ast_xml_get_attribute(tmp, "name");
1381  if (valname) {
1382  ret = 1;
1383  ast_str_append(buffer, 0, "%s<value>%s</value>", tabs, valname);
1384  ast_xml_free_attr(valname);
1385  }
1386  tmptext = ast_xml_get_text(tmp);
1387  /* Check inside this node for any explanation about its meaning. */
1388  if (tmptext) {
1389  /* Cleanup text. */
1390  xmldoc_string_cleanup(tmptext, &cleanstr, 1);
1391  ast_xml_free_text(tmptext);
1392  if (cleanstr && ast_str_strlen(cleanstr) > 0) {
1393  ast_str_append(buffer, 0, ":%s", ast_str_buffer(cleanstr));
1394  }
1395  ast_free(cleanstr);
1396  }
1397  ast_str_append(buffer, 0, "\n");
1398  }
1399 
1400  return ret;
1401 }
1402 
1403 /*! \internal
1404  * \brief Parse a <variablelist> node and put all the output inside 'buffer'.
1405  * \param node The variablelist node pointer.
1406  * \param tabs A string to be appended at the begining of the output that will be stored
1407  * in buffer.
1408  * \param buffer This must be an already created ast_str. It will be used
1409  * to store the result (if already has something it will be appended to the current
1410  * string).
1411  * \retval 1 If a <variablelist> element is parsed.
1412  * \retval 0 On error.
1413  */
1414 static int xmldoc_parse_variablelist(struct ast_xml_node *node, const char *tabs, struct ast_str **buffer)
1415 {
1416  struct ast_xml_node *tmp;
1417  const char *varname;
1418  char *vartabs;
1419  int ret = 0;
1420 
1421  if (!node || !ast_xml_node_get_children(node)) {
1422  return ret;
1423  }
1424 
1425  if (strcasecmp(ast_xml_node_get_name(node), "variablelist")) {
1426  return ret;
1427  }
1428 
1429  /* use this spacing (add 4 spaces) inside a variablelist node. */
1430  if (ast_asprintf(&vartabs, "%s ", tabs) < 0) {
1431  return ret;
1432  }
1433  for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
1434  /* We can have a <para> element inside the variable list */
1435  if ((xmldoc_parse_para(tmp, (ret ? tabs : ""), "\n", buffer))) {
1436  ret = 1;
1437  continue;
1438  } else if ((xmldoc_parse_specialtags(tmp, (ret ? tabs : ""), "\n", buffer))) {
1439  ret = 1;
1440  continue;
1441  }
1442 
1443  if (!strcasecmp(ast_xml_node_get_name(tmp), "variable")) {
1444  /* Store the variable name in buffer. */
1445  varname = ast_xml_get_attribute(tmp, "name");
1446  if (varname) {
1447  ast_str_append(buffer, 0, "%s<variable>%s</variable>: ", tabs, varname);
1448  ast_xml_free_attr(varname);
1449  /* Parse the <variable> possible values. */
1450  xmldoc_parse_variable(tmp, vartabs, buffer);
1451  ret = 1;
1452  }
1453  }
1454  }
1455 
1456  ast_free(vartabs);
1457 
1458  return ret;
1459 }
1460 
1461 char *ast_xmldoc_build_seealso(const char *type, const char *name, const char *module)
1462 {
1463  struct ast_str *outputstr;
1464  char *output;
1465  struct ast_xml_node *node;
1466  const char *typename;
1467  const char *content;
1468  int first = 1;
1469 
1470  if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
1471  return NULL;
1472  }
1473 
1474  /* get the application/function root node. */
1475  node = xmldoc_get_node(type, name, module, documentation_language);
1476  if (!node || !ast_xml_node_get_children(node)) {
1477  return NULL;
1478  }
1479 
1480  /* Find the <see-also> node. */
1481  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1482  if (!strcasecmp(ast_xml_node_get_name(node), "see-also")) {
1483  break;
1484  }
1485  }
1486 
1487  if (!node || !ast_xml_node_get_children(node)) {
1488  /* we couldnt find a <see-also> node. */
1489  return NULL;
1490  }
1491 
1492  /* prepare the output string. */
1493  outputstr = ast_str_create(128);
1494  if (!outputstr) {
1495  return NULL;
1496  }
1497 
1498  /* get into the <see-also> node. */
1499  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1500  if (strcasecmp(ast_xml_node_get_name(node), "ref")) {
1501  continue;
1502  }
1503 
1504  /* parse the <ref> node. 'type' attribute is required. */
1505  typename = ast_xml_get_attribute(node, "type");
1506  if (!typename) {
1507  continue;
1508  }
1509  content = ast_xml_get_text(node);
1510  if (!content) {
1511  ast_xml_free_attr(typename);
1512  continue;
1513  }
1514  if (!strcasecmp(typename, "application")) {
1515  ast_str_append(&outputstr, 0, "%s%s()", (first ? "" : ", "), content);
1516  } else if (!strcasecmp(typename, "function")) {
1517  ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content);
1518  } else if (!strcasecmp(typename, "astcli")) {
1519  ast_str_append(&outputstr, 0, "%s<astcli>%s</astcli>", (first ? "" : ", "), content);
1520  } else {
1521  ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content);
1522  }
1523  first = 0;
1524  ast_xml_free_text(content);
1525  ast_xml_free_attr(typename);
1526  }
1527 
1528  output = ast_strdup(ast_str_buffer(outputstr));
1529  ast_free(outputstr);
1530 
1531  return output;
1532 }
1533 
1534 /*! \internal
1535  * \brief Parse a <enum> node.
1536  * \brief fixnode An ast_xml_node pointer to the <enum> node.
1537  * \bried buffer The output buffer.
1538  * \retval 0 if content is not found inside the enum element (data is not appended to buffer).
1539  * \retval 1 if content is found and data is appended to buffer.
1540  */
1541 static int xmldoc_parse_enum(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
1542 {
1543  struct ast_xml_node *node = fixnode;
1544  int ret = 0;
1545  char *optiontabs;
1546 
1547  if (ast_asprintf(&optiontabs, "%s ", tabs) < 0) {
1548  return ret;
1549  }
1550 
1551  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1552  if ((xmldoc_parse_para(node, (ret ? tabs : " - "), "\n", buffer))) {
1553  ret = 1;
1554  } else if ((xmldoc_parse_specialtags(node, (ret ? tabs : " - "), "\n", buffer))) {
1555  ret = 1;
1556  }
1557 
1558  xmldoc_parse_enumlist(node, optiontabs, buffer);
1559  }
1560 
1561  ast_free(optiontabs);
1562 
1563  return ret;
1564 }
1565 
1566 /*! \internal
1567  * \brief Parse a <enumlist> node.
1568  * \param fixnode As ast_xml pointer to the <enumlist> node.
1569  * \param buffer The ast_str output buffer.
1570  * \retval 0 if no <enumlist> node was parsed.
1571  * \retval 1 if a <enumlist> node was parsed.
1572  */
1573 static int xmldoc_parse_enumlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
1574 {
1575  struct ast_xml_node *node = fixnode;
1576  const char *enumname;
1577  int ret = 0;
1578 
1579  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1580  if (strcasecmp(ast_xml_node_get_name(node), "enum")) {
1581  continue;
1582  }
1583 
1584  enumname = ast_xml_get_attribute(node, "name");
1585  if (enumname) {
1586  ast_str_append(buffer, 0, "%s<enum>%s</enum>", tabs, enumname);
1587  ast_xml_free_attr(enumname);
1588 
1589  /* parse only enum elements inside a enumlist node. */
1590  if ((xmldoc_parse_enum(node, tabs, buffer))) {
1591  ret = 1;
1592  } else {
1593  ast_str_append(buffer, 0, "\n");
1594  }
1595  }
1596  }
1597  return ret;
1598 }
1599 
1600 /*! \internal
1601  * \brief Parse an <option> node.
1602  * \param fixnode An ast_xml pointer to the <option> node.
1603  * \param tabs A string to be appended at the begining of each line being added to the
1604  * buffer string.
1605  * \param buffer The output buffer.
1606  * \retval 0 if no option node is parsed.
1607  * \retval 1 if an option node is parsed.
1608  */
1609 static int xmldoc_parse_option(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
1610 {
1611  struct ast_xml_node *node;
1612  int ret = 0;
1613  char *optiontabs;
1614 
1615  if (ast_asprintf(&optiontabs, "%s ", tabs) < 0) {
1616  return ret;
1617  }
1618  for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
1619  if (!strcasecmp(ast_xml_node_get_name(node), "argument")) {
1620  /* if this is the first data appended to buffer, print a \n*/
1621  if (!ret && ast_xml_node_get_children(node)) {
1622  /* print \n */
1623  ast_str_append(buffer, 0, "\n");
1624  }
1625  if (xmldoc_parse_argument(node, 0, NULL, optiontabs, buffer)) {
1626  ret = 1;
1627  }
1628  continue;
1629  }
1630 
1631  if (xmldoc_parse_para(node, (ret ? tabs : ""), "\n", buffer)) {
1632  ret = 1;
1633  } else if (xmldoc_parse_specialtags(node, (ret ? tabs : ""), "\n", buffer)) {
1634  ret = 1;
1635  }
1636 
1637  xmldoc_parse_variablelist(node, optiontabs, buffer);
1638 
1639  xmldoc_parse_enumlist(node, optiontabs, buffer);
1640  }
1641  ast_free(optiontabs);
1642 
1643  return ret;
1644 }
1645 
1646 /*! \internal
1647  * \brief Parse an <optionlist> element from the xml documentation.
1648  * \param fixnode Pointer to the optionlist xml node.
1649  * \param tabs A string to be appended at the begining of each line being added to the
1650  * buffer string.
1651  * \param buffer Output buffer to put what is inside the optionlist tag.
1652  */
1653 static void xmldoc_parse_optionlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
1654 {
1655  struct ast_xml_node *node;
1656  const char *optname, *hasparams;
1657  char *optionsyntax;
1658  int optparams;
1659 
1660  for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
1661  /* Start appending every option tag. */
1662  if (strcasecmp(ast_xml_node_get_name(node), "option")) {
1663  continue;
1664  }
1665 
1666  /* Get the option name. */
1667  optname = ast_xml_get_attribute(node, "name");
1668  if (!optname) {
1669  continue;
1670  }
1671 
1672  optparams = 1;
1673  hasparams = ast_xml_get_attribute(node, "hasparams");
1674  if (hasparams && !strcasecmp(hasparams, "optional")) {
1675  optparams = 2;
1676  }
1677 
1678  optionsyntax = xmldoc_get_syntax_fun(node, optname, "argument", 0, optparams);
1679  if (!optionsyntax) {
1680  ast_xml_free_attr(optname);
1681  ast_xml_free_attr(hasparams);
1682  continue;
1683  }
1684 
1685  ast_str_append(buffer, 0, "%s%s: ", tabs, optionsyntax);
1686 
1687  if (!xmldoc_parse_option(node, tabs, buffer)) {
1688  ast_str_append(buffer, 0, "\n");
1689  }
1690  ast_str_append(buffer, 0, "\n");
1691  ast_xml_free_attr(optname);
1692  ast_xml_free_attr(hasparams);
1693  ast_free(optionsyntax);
1694  }
1695 }
1696 
1697 /*! \internal
1698  * \brief Parse a 'parameter' tag inside a syntax element.
1699  * \param fixnode A pointer to the 'parameter' xml node.
1700  * \param tabs A string to be appended at the beginning of each line being printed inside
1701  * 'buffer'.
1702  * \param buffer String buffer to put values found inside the parameter element.
1703  */
1704 static void xmldoc_parse_parameter(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
1705 {
1706  const char *paramname;
1707  struct ast_xml_node *node = fixnode;
1708  int hasarguments, printed = 0;
1709  char *internaltabs;
1710 
1711  if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
1712  return;
1713  }
1714 
1715  hasarguments = xmldoc_has_inside(node, "argument");
1716  if (!(paramname = ast_xml_get_attribute(node, "name"))) {
1717  /* parameter MUST have an attribute name. */
1718  return;
1719  }
1720 
1721  if (ast_asprintf(&internaltabs, "%s ", tabs) < 0) {
1722  ast_xml_free_attr(paramname);
1723  return;
1724  }
1725 
1726  if (!hasarguments && xmldoc_has_nodes(node)) {
1727  ast_str_append(buffer, 0, "%s\n", paramname);
1728  ast_xml_free_attr(paramname);
1729  printed = 1;
1730  }
1731 
1732  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1733  if (!strcasecmp(ast_xml_node_get_name(node), "optionlist")) {
1734  xmldoc_parse_optionlist(node, internaltabs, buffer);
1735  } else if (!strcasecmp(ast_xml_node_get_name(node), "enumlist")) {
1736  xmldoc_parse_enumlist(node, internaltabs, buffer);
1737  } else if (!strcasecmp(ast_xml_node_get_name(node), "argument")) {
1738  xmldoc_parse_argument(node, 1, internaltabs, (!hasarguments ? " " : ""), buffer);
1739  } else if (!strcasecmp(ast_xml_node_get_name(node), "para")) {
1740  if (!printed) {
1741  ast_str_append(buffer, 0, "%s\n", paramname);
1742  ast_xml_free_attr(paramname);
1743  printed = 1;
1744  }
1745  if (xmldoc_parse_para(node, internaltabs, "\n", buffer)) {
1746  /* If anything ever goes in below this condition before the continue below,
1747  * we should probably continue immediately. */
1748  continue;
1749  }
1750  continue;
1751  } else if ((xmldoc_parse_specialtags(node, internaltabs, "\n", buffer))) {
1752  continue;
1753  }
1754  }
1755  if (!printed) {
1756  ast_xml_free_attr(paramname);
1757  }
1758  ast_free(internaltabs);
1759 }
1760 
1761 char *ast_xmldoc_build_arguments(const char *type, const char *name, const char *module)
1762 {
1763  struct ast_xml_node *node;
1764  struct ast_str *ret = ast_str_create(128);
1765  char *retstr = NULL;
1766 
1767  if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
1768  ast_free(ret);
1769  return NULL;
1770  }
1771 
1772  node = xmldoc_get_node(type, name, module, documentation_language);
1773 
1774  if (!node || !ast_xml_node_get_children(node)) {
1775  ast_free(ret);
1776  return NULL;
1777  }
1778 
1779  /* Find the syntax field. */
1780  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1781  if (!strcasecmp(ast_xml_node_get_name(node), "syntax")) {
1782  break;
1783  }
1784  }
1785 
1786  if (!node || !ast_xml_node_get_children(node)) {
1787  /* We couldn't find the syntax node. */
1788  ast_free(ret);
1789  return NULL;
1790  }
1791 
1792  for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
1793  xmldoc_parse_parameter(node, "", &ret);
1794  }
1795 
1796  if (ast_str_strlen(ret) > 0) {
1797  /* remove last '\n' */
1798  char *buf = ast_str_buffer(ret);
1799  if (buf[ast_str_strlen(ret) - 1] == '\n') {
1800  ast_str_truncate(ret, -1);
1801  }
1802  retstr = ast_strdup(ast_str_buffer(ret));
1803  }
1804  ast_free(ret);
1805 
1806  return retstr;
1807 }
1808 
1809 /*! \internal
1810  * \brief Return the string within a node formatted with <para> and <variablelist> elements.
1811  * \param node Parent node where content resides.
1812  * \param raw If set, return the node's content without further processing.
1813  * \param raw_wrap Wrap raw text.
1814  * \retval NULL on error
1815  * \retval Node content on success.
1816  */
1817 static struct ast_str *xmldoc_get_formatted(struct ast_xml_node *node, int raw_output, int raw_wrap)
1818 {
1819  struct ast_xml_node *tmp;
1820  const char *notcleanret, *tmpstr;
1821  struct ast_str *ret;
1822 
1823  if (raw_output) {
1824  /* xmldoc_string_cleanup will allocate the ret object */
1825  notcleanret = ast_xml_get_text(node);
1826  tmpstr = notcleanret;
1827  xmldoc_string_cleanup(ast_skip_blanks(notcleanret), &ret, 0);
1828  ast_xml_free_text(tmpstr);
1829  } else {
1830  ret = ast_str_create(128);
1831  for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
1832  /* if found, parse a <para> element. */
1833  if (xmldoc_parse_para(tmp, "", "\n", &ret)) {
1834  continue;
1835  } else if (xmldoc_parse_specialtags(tmp, "", "\n", &ret)) {
1836  continue;
1837  }
1838  /* if found, parse a <variablelist> element. */
1839  xmldoc_parse_variablelist(tmp, "", &ret);
1840  xmldoc_parse_enumlist(tmp, " ", &ret);
1841  }
1842  /* remove last '\n' */
1843  /* XXX Don't modify ast_str internals manually */
1844  tmpstr = ast_str_buffer(ret);
1845  if (tmpstr[ast_str_strlen(ret) - 1] == '\n') {
1846  ast_str_truncate(ret, -1);
1847  }
1848  }
1849  return ret;
1850 }
1851 
1852 /*!
1853  * \brief Get the content of a field (synopsis, description, etc) from an asterisk document tree
1854  * \param type Type of element (application, function, ...).
1855  * \param name Name of element (Dial, Echo, Playback, ...).
1856  * \param var Name of field to return (synopsis, description, etc).
1857  * \param raw Field only contains text, no other elements inside it.
1858  * \retval NULL On error.
1859  * \retval Field text content on success.
1860  */
1861 static char *xmldoc_build_field(const char *type, const char *name, const char *module, const char *var, int raw)
1862 {
1863  struct ast_xml_node *node;
1864  char *ret = NULL;
1865  struct ast_str *formatted;
1866 
1867  if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
1868  ast_log(LOG_ERROR, "Tried to look in XML tree with faulty values.\n");
1869  return ret;
1870  }
1871 
1872  node = xmldoc_get_node(type, name, module, documentation_language);
1873 
1874  if (!node) {
1875  ast_log(LOG_WARNING, "Couldn't find %s %s in XML documentation\n", type, name);
1876  return ret;
1877  }
1878 
1879  node = ast_xml_find_element(ast_xml_node_get_children(node), var, NULL, NULL);
1880 
1881  if (!node || !ast_xml_node_get_children(node)) {
1882  return ret;
1883  }
1884 
1885  formatted = xmldoc_get_formatted(node, raw, raw);
1886  if (ast_str_strlen(formatted) > 0) {
1887  ret = ast_strdup(ast_str_buffer(formatted));
1888  }
1889  ast_free(formatted);
1890 
1891  return ret;
1892 }
1893 
1894 char *ast_xmldoc_build_synopsis(const char *type, const char *name, const char *module)
1895 {
1896  return xmldoc_build_field(type, name, module, "synopsis", 1);
1897 }
1898 
1899 char *ast_xmldoc_build_description(const char *type, const char *name, const char *module)
1900 {
1901  return xmldoc_build_field(type, name, module, "description", 0);
1902 }
1903 
1904 #if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
1905 static int xml_pathmatch(char *xmlpattern, int xmlpattern_maxlen, glob_t *globbuf)
1906 {
1907  int globret;
1908 
1909  snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/thirdparty/*-%s.xml",
1910  ast_config_AST_DATA_DIR, documentation_language);
1911  if((globret = glob(xmlpattern, GLOB_NOCHECK, NULL, globbuf))) {
1912  return globret;
1913  }
1914 
1915  snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/thirdparty/*-%.2s_??.xml",
1916  ast_config_AST_DATA_DIR, documentation_language);
1917  if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
1918  return globret;
1919  }
1920 
1921  snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/thirdparty/*-%s.xml",
1922  ast_config_AST_DATA_DIR, default_documentation_language);
1923  if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
1924  return globret;
1925  }
1926 
1927  snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/*-%s.xml",
1928  ast_config_AST_DATA_DIR, documentation_language);
1929  if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
1930  return globret;
1931  }
1932 
1933  snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/*-%.2s_??.xml",
1934  ast_config_AST_DATA_DIR, documentation_language);
1935  if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
1936  return globret;
1937  }
1938 
1939  snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/*-%s.xml",
1940  ast_config_AST_DATA_DIR, default_documentation_language);
1941  globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf);
1942 
1943  return globret;
1944 }
1945 #endif
1946 
1947 /*! \brief Close and unload XML documentation. */
1949 {
1950  struct documentation_tree *doctree;
1951 
1953  while ((doctree = AST_RWLIST_REMOVE_HEAD(&xmldoc_tree, entry))) {
1954  ast_free(doctree->filename);
1955  ast_xml_close(doctree->doc);
1956  ast_free(doctree);
1957  }
1959 
1960  ast_xml_finish();
1961 }
1962 
1964 {
1965  struct ast_xml_node *root_node;
1966  struct ast_xml_doc *tmpdoc;
1967  struct documentation_tree *doc_tree;
1968  char *xmlpattern;
1969  struct ast_config *cfg = NULL;
1970  struct ast_variable *var = NULL;
1971  struct ast_flags cnfflags = { 0 };
1972  int globret, i, dup, duplicate;
1973  glob_t globbuf;
1974 #if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
1975  int xmlpattern_maxlen;
1976 #endif
1977 
1978  /* setup default XML documentation language */
1979  snprintf(documentation_language, sizeof(documentation_language), default_documentation_language);
1980 
1981  if ((cfg = ast_config_load2("asterisk.conf", "" /* core can't reload */, cnfflags)) && cfg != CONFIG_STATUS_FILEINVALID) {
1982  for (var = ast_variable_browse(cfg, "options"); var; var = var->next) {
1983  if (!strcasecmp(var->name, "documentation_language")) {
1984  if (!ast_strlen_zero(var->value)) {
1985  snprintf(documentation_language, sizeof(documentation_language), "%s", var->value);
1986  }
1987  }
1988  }
1989  ast_config_destroy(cfg);
1990  }
1991 
1992  /* initialize the XML library. */
1993  ast_xml_init();
1994 
1995  /* register function to be run when asterisk finish. */
1997 
1998  globbuf.gl_offs = 0; /* slots to reserve in gl_pathv */
1999 
2000 #if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
2001  xmlpattern_maxlen = strlen(ast_config_AST_DATA_DIR) + strlen("/documentation/thirdparty") + strlen("/*-??_??.xml") + 1;
2002  xmlpattern = ast_malloc(xmlpattern_maxlen);
2003  globret = xml_pathmatch(xmlpattern, xmlpattern_maxlen, &globbuf);
2004 #else
2005  /* Get every *-LANG.xml file inside $(ASTDATADIR)/documentation */
2006  if (ast_asprintf(&xmlpattern, "%s/documentation{/thirdparty/,/}*-{%s,%.2s_??,%s}.xml", ast_config_AST_DATA_DIR,
2007  documentation_language, documentation_language, default_documentation_language) < 0) {
2008  return 1;
2009  }
2010  globret = glob(xmlpattern, MY_GLOB_FLAGS, NULL, &globbuf);
2011 #endif
2012 
2013  ast_debug(3, "gl_pathc %zu\n", globbuf.gl_pathc);
2014  if (globret == GLOB_NOSPACE) {
2015  ast_log(LOG_WARNING, "XML load failure, glob expansion of pattern '%s' failed: Not enough memory\n", xmlpattern);
2016  ast_free(xmlpattern);
2017  return 1;
2018  } else if (globret == GLOB_ABORTED) {
2019  ast_log(LOG_WARNING, "XML load failure, glob expansion of pattern '%s' failed: Read error\n", xmlpattern);
2020  ast_free(xmlpattern);
2021  return 1;
2022  }
2023  ast_free(xmlpattern);
2024 
2026  /* loop over expanded files */
2027  for (i = 0; i < globbuf.gl_pathc; i++) {
2028  /* check for duplicates (if we already [try to] open the same file. */
2029  duplicate = 0;
2030  for (dup = 0; dup < i; dup++) {
2031  if (!strcmp(globbuf.gl_pathv[i], globbuf.gl_pathv[dup])) {
2032  duplicate = 1;
2033  break;
2034  }
2035  }
2036  if (duplicate || strchr(globbuf.gl_pathv[i], '*')) {
2037  /* skip duplicates as well as pathnames not found
2038  * (due to use of GLOB_NOCHECK in xml_pathmatch) */
2039  continue;
2040  }
2041  tmpdoc = NULL;
2042  tmpdoc = ast_xml_open(globbuf.gl_pathv[i]);
2043  if (!tmpdoc) {
2044  ast_log(LOG_ERROR, "Could not open XML documentation at '%s'\n", globbuf.gl_pathv[i]);
2045  continue;
2046  }
2047  /* Get doc root node and check if it starts with '<docs>' */
2048  root_node = ast_xml_get_root(tmpdoc);
2049  if (!root_node) {
2050  ast_log(LOG_ERROR, "Error getting documentation root node\n");
2051  ast_xml_close(tmpdoc);
2052  continue;
2053  }
2054  /* Check root node name for malformed xmls. */
2055  if (strcmp(ast_xml_node_get_name(root_node), "docs")) {
2056  ast_log(LOG_ERROR, "Documentation file is not well formed!\n");
2057  ast_xml_close(tmpdoc);
2058  continue;
2059  }
2060  doc_tree = ast_calloc(1, sizeof(*doc_tree));
2061  if (!doc_tree) {
2062  ast_log(LOG_ERROR, "Unable to allocate documentation_tree structure!\n");
2063  ast_xml_close(tmpdoc);
2064  continue;
2065  }
2066  doc_tree->doc = tmpdoc;
2067  doc_tree->filename = ast_strdup(globbuf.gl_pathv[i]);
2068  AST_RWLIST_INSERT_TAIL(&xmldoc_tree, doc_tree, entry);
2069  }
2071 
2072  globfree(&globbuf);
2073 
2074  return 0;
2075 }
2076 
2077 #endif /* AST_XML_DOCS */
2078 
2079 
static int xmldoc_parse_option(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
Definition: xmldoc.c:1609
XML documentation tree.
Definition: xmldoc.c:60
Asterisk main include file. File version handling, generic pbx functions.
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
int ast_xml_init(void)
Initialize the XML library implementation. This function is used to setup everything needed to start ...
Definition: xml.c:41
#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:332
String manipulation functions.
#define COLOR_GRAY
Definition: term.h:48
#define ast_strdup(a)
Definition: astmm.h:109
const char * type
Definition: xmldoc.c:1130
#define ast_vasprintf(a, b, c)
Definition: astmm.h:127
static char * xmldoc_build_field(const char *type, const char *name, const char *module, const char *var, int raw)
Get the content of a field (synopsis, description, etc) from an asterisk document tree...
Definition: xmldoc.c:1861
static void xmldoc_parse_optionlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
Definition: xmldoc.c:1653
#define COLOR_YELLOW
Definition: term.h:54
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
static int xmldoc_foundspace_backward(const char *text, int currentpos, int maxdiff)
Definition: xmldoc.c:209
static int xmldoc_attribute_match(struct ast_xml_node *node, const char *attr, const char *value)
Definition: xmldoc.c:476
#define LOG_WARNING
Definition: logger.h:144
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
Definition: config.c:597
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
struct ast_xml_doc * ast_xml_open(char *filename)
Open an XML document.
Definition: xml.c:55
#define COLOR_CYAN
Definition: term.h:59
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
char * ast_xmldoc_build_description(const char *type, const char *name, const char *module)
Generate description documentation from XML.
Definition: xmldoc.c:1899
static struct ast_str * xmldoc_get_formatted(struct ast_xml_node *node, int raw_output, int raw_wrap)
Definition: xmldoc.c:1817
Mapping between type of node and type of syntax to generate.
Definition: xmldoc.c:1129
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
#define var
Definition: ast_expr2f.c:606
int ast_term_color_code(struct ast_str **str, int fgcolor, int bgcolor)
Append a color sequence to an ast_str.
Definition: term.c:242
static int xmldoc_wait_nextspace(const char *text, int currentpos, int maxdiff)
Definition: xmldoc.c:169
const char * init
Definition: xmldoc.c:102
static void xmldoc_reverse_helper(int reverse, int *len, char **syntax, const char *fmt,...)
Definition: xmldoc.c:575
Configuration File Parser.
static char * xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *name, int printname)
Definition: xmldoc.c:974
int ast_xmldoc_load_documentation(void)
Load XML documentation. Provided by xmldoc.c.
Definition: xmldoc.c:1963
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:900
static int xmldoc_parse_enum(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
Definition: xmldoc.c:1541
struct ast_str * ast_str_create(size_t init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:420
#define COLOR_GREEN
Definition: term.h:51
char * text
Definition: app_queue.c:1091
struct ast_xml_node * ast_xml_get_root(struct ast_xml_doc *doc)
Get the document root node.
Definition: xml.c:153
char * ast_xmldoc_build_synopsis(const char *type, const char *name, const char *module)
Generate synopsis documentation from XML.
Definition: xmldoc.c:1894
int value
Definition: syslog.c:39
const int colorfg
Definition: xmldoc.c:81
char * ast_xmldoc_build_arguments(const char *type, const char *name, const char *module)
Generate the [arguments] tag based on type of node (&#39;application&#39;, &#39;function&#39; or &#39;agi&#39;) and name...
Definition: xmldoc.c:1761
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
Definition: config.c:2499
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
static char * xmldoc_string_wrap(const char *text, int columns, int maxdiff)
Definition: xmldoc.c:238
char * ast_str_truncate(struct ast_str *buf, ssize_t len)
Truncates the enclosed string to the given length.
Definition: strings.h:521
static int xmldoc_has_specialtags(struct ast_xml_node *fixnode)
Definition: xmldoc.c:660
static struct strsyntaxtype stxtype[]
static int xmldoc_has_nodes(struct ast_xml_node *fixnode)
Definition: xmldoc.c:642
const char * ast_xml_get_attribute(struct ast_xml_node *node, const char *attrname)
Get a node attribute by name.
Definition: xml.c:190
void ast_xml_free_attr(const char *attribute)
Free an attribute returned by ast_xml_get_attribute()
Definition: xml.c:176
syntaxtype
Types of syntax that we are able to generate.
Definition: xmldoc.c:1122
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
#define ast_asprintf(a, b, c...)
Definition: astmm.h:121
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
const char * value
Definition: config.h:79
static struct strspecial_tags special_tags[]
Asterisk file paths, configured in asterisk.conf.
static const char default_documentation_language[]
Default documentation language.
Definition: xmldoc.c:45
#define ISLAST(__rev, __a)
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
static char * xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char *name)
Definition: xmldoc.c:1072
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
Definition: asterisk.c:998
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:220
A set of macros to manage forward-linked lists.
static char language[MAX_LANGUAGE]
Definition: chan_alsa.c:108
const char * name
Definition: config.h:77
const char * endtag
Definition: xmldoc.c:83
const char * tagname
Definition: xmldoc.c:101
struct ast_xml_doc * doc
Definition: xmldoc.c:62
static const int xmldoc_max_diff
This is a value that we will use to let the wrapping mechanism move the cursor backward and forward x...
Definition: xmldoc.c:54
static int xmldoc_parse_specialtags(struct ast_xml_node *fixnode, const char *tabs, const char *posttabs, struct ast_str **buffer)
Definition: xmldoc.c:1256
static struct strcolorized_tags colorized_tags[]
#define COLOR_RED
Definition: term.h:49
static char documentation_language[6]
XML documentation language.
Definition: xmldoc.c:57
const char * end
Definition: xmldoc.c:80
const char * ast_config_AST_DATA_DIR
Definition: asterisk.c:262
char * filename
Definition: xmldoc.c:61
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
#define LOG_ERROR
Definition: logger.h:155
static void xmldoc_parse_parameter(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
Definition: xmldoc.c:1704
static int xmldoc_parse_enumlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
Definition: xmldoc.c:1573
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is &quot;true&quot;. This function checks to see whether a string passed to it is an indication of an &quot;true&quot; value. It checks to see if the string is &quot;yes&quot;, &quot;true&quot;, &quot;y&quot;, &quot;t&quot;, &quot;on&quot; or &quot;1&quot;.
Definition: utils.c:1533
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
static enum syntaxtype xmldoc_get_syntax_type(const char *type)
Definition: xmldoc.c:1144
static const int xmldoc_text_columns
Number of columns to print when showing the XML documentation with a &#39;core show application/function ...
Definition: xmldoc.c:49
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:97
struct sla_ringing_trunk * first
Definition: app_meetme.c:965
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define MY_GLOB_FLAGS
Definition: compat.h:214
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
static void xmldoc_unload_documentation(void)
Close and unload XML documentation.
Definition: xmldoc.c:1948
static int xmldoc_parse_argument(struct ast_xml_node *fixnode, int insideparameter, const char *paramtabs, const char *tabs, struct ast_str **buffer)
Definition: xmldoc.c:1306
static const char name[]
#define ast_free(a)
Definition: astmm.h:97
#define GLOB_ABORTED
Definition: ael_lex.c:830
#define GOTONEXT(__rev, __a)
static void xmldoc_string_cleanup(const char *text, struct ast_str **output, int lastspaces)
Definition: xmldoc.c:431
static char * xmldoc_parse_cmd_enumlist(struct ast_xml_node *fixnode)
Definition: xmldoc.c:927
static int xmldoc_has_inside(struct ast_xml_node *fixnode, const char *what)
Definition: xmldoc.c:624
Asterisk XML Documentation API.
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:829
char * ast_xmldoc_printable(const char *bwinput, int withcolors)
Colorize and put delimiters (instead of tags) to the xmldoc output.
Definition: xmldoc.c:315
#define ast_opt_light_background
Definition: options.h:128
void ast_xml_close(struct ast_xml_doc *doc)
Close an already open document and free the used structure.
Definition: xml.c:134
static const char type[]
Definition: chan_nbs.c:57
Structure used to handle boolean flags.
Definition: utils.h:200
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:1156
char * ast_xmldoc_build_seealso(const char *type, const char *name, const char *module)
Parse the &lt;see-also&gt; node content.
Definition: xmldoc.c:1461
#define AST_RWLIST_ENTRY
Definition: linkedlists.h:414
enum syntaxtype stxtype
Definition: xmldoc.c:1131
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:471
#define MP(__a)
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:726
static void xmldoc_setpostbr(char *postbr, size_t len, const char *text)
Definition: xmldoc.c:141
#define ast_calloc(a, b)
Definition: astmm.h:82
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
const char * end
Definition: xmldoc.c:103
#define ast_realloc(a, b)
Definition: astmm.h:103
const char * ast_xml_get_text(struct ast_xml_node *node)
Get an element content string.
Definition: xml.c:271
const char * inittag
Definition: xmldoc.c:82
Handy terminal functions for vt* terms.
struct ast_variable * next
Definition: config.h:82
static int xmldoc_parse_para(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer)
Definition: xmldoc.c:1202
Container of documentation trees.
Definition: xmldoc.c:76
void ast_str_trim_blanks(struct ast_str *buf)
Trims trailing whitespace characters from an ast_str string.
Definition: strings.h:461
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
#define COLOR_BLUE
Definition: term.h:55
#define ESC
Definition: term.h:30
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:304
#define ast_malloc(a)
Definition: astmm.h:91
char * strcasestr(const char *, const char *)
static struct ast_xml_node * xmldoc_get_node(const char *type, const char *name, const char *module, const char *language)
Definition: xmldoc.c:495
char * term_end(void)
Definition: term.c:365
static snd_pcm_format_t format
Definition: chan_alsa.c:93
int ast_xml_finish(void)
Cleanup library allocated global data.
Definition: xml.c:48
static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
Definition: chan_iax2.c:2069
static int xmldoc_parse_variablelist(struct ast_xml_node *node, const char *tabs, struct ast_str **buffer)
Definition: xmldoc.c:1414
static int xmldoc_parse_variable(struct ast_xml_node *node, const char *tabs, struct ast_str **buffer)
Definition: xmldoc.c:1353
struct ast_xml_node * ast_xml_node_get_children(struct ast_xml_node *node)
Get the node&#39;s children.
Definition: xml.c:299
const char * ast_xml_node_get_name(struct ast_xml_node *node)
Get the name of a node.
Definition: xml.c:294
void ast_xml_free_text(const char *text)
Free a content element that was returned by ast_xml_get_text()
Definition: xml.c:183
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
static char * xmldoc_get_syntax_fun(struct ast_xml_node *rootnode, const char *rootname, const char *childname, int printparenthesis, int printrootname)
Definition: xmldoc.c:685
static int xmldoc_postbrlen(const char *postbr)
Definition: xmldoc.c:115
const char * init
Definition: xmldoc.c:79