xmldoc.c 84 KB

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