summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/resources/webidl2/test/widlproc/src/comment.c
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/resources/webidl2/test/widlproc/src/comment.c')
-rw-r--r--testing/web-platform/tests/resources/webidl2/test/widlproc/src/comment.c1827
1 files changed, 1827 insertions, 0 deletions
diff --git a/testing/web-platform/tests/resources/webidl2/test/widlproc/src/comment.c b/testing/web-platform/tests/resources/webidl2/test/widlproc/src/comment.c
new file mode 100644
index 000000000..622a7bea7
--- /dev/null
+++ b/testing/web-platform/tests/resources/webidl2/test/widlproc/src/comment.c
@@ -0,0 +1,1827 @@
+/***********************************************************************
+ * $Id$
+ * Copyright 2009 Aplix Corporation. All rights reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ***********************************************************************/
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "comment.h"
+#include "entities.h"
+#include "lex.h"
+#include "misc.h"
+#include "node.h"
+#include "os.h"
+#include "process.h"
+
+/* struct cnode : a node in the comment parse tree */
+struct cnode {
+ struct cnode *next;
+ struct cnode *children;
+ struct cnode *parent;
+ const struct cnodefuncs *funcs;
+ const char *attrtext;
+ const char *filename;
+ unsigned int linenum;
+};
+
+struct cnodefuncs {
+ int indesc; /* non-zero if it outputs its own xml element that does
+ not want to be inside <description> */
+ int needpara; /* non-zero if text must be in a para node that is a child
+ of this one */
+ int (*askend)(struct cnode *cnode, const struct cnodefuncs *type);
+ void (*end)(struct cnode *cnode);
+ void (*output)(struct cnode *cnode, unsigned int indent);
+};
+
+struct paramcnode {
+ struct cnode cn;
+ int inout;
+ char name[1];
+};
+
+/* struct comment : a doxygen comment */
+struct comment {
+ struct comment *next;
+ struct node *node;
+ unsigned int type;
+ const char *filename;
+ unsigned int linenum;
+ struct cnode root;
+ int back; /* Whether the comment refers back rather than forward. */
+ char *text;
+};
+
+
+static struct node *lastidentifier;
+static struct comment *comments;
+static int incode, inhtmlblock;
+static struct comment *curcomment;
+
+/***********************************************************************
+ * htmleldescs : table of recnogized HTML elements
+ */
+#define HTMLEL_EMPTY 1
+#define HTMLEL_INLINE 2
+#define HTMLEL_BLOCK 4
+#define HTMLEL_AUTOCLOSE 8
+#define HTMLEL_LI 0x10
+#define HTMLEL_DLCONTENTS 0x20
+#define HTMLEL_TABLECONTENTS 0x40
+#define HTMLEL_TRCONTENTS 0x80
+
+#define HTMLEL_FLOW (HTMLEL_BLOCK | HTMLEL_INLINE)
+
+struct htmleldesc {
+ unsigned int namelen;
+ const char *name;
+ unsigned int flags;
+ unsigned int content;
+};
+
+static const struct htmleldesc htmleldescs[] = {
+ { 1, "a", HTMLEL_INLINE, 0 },
+ { 1, "b", HTMLEL_INLINE, 0 },
+ { 2, "br", HTMLEL_INLINE, HTMLEL_EMPTY },
+ { 3, "img", HTMLEL_INLINE, HTMLEL_EMPTY },
+ { 2, "dd", HTMLEL_DLCONTENTS, HTMLEL_FLOW },
+ { 2, "dl", HTMLEL_BLOCK, HTMLEL_DLCONTENTS },
+ { 2, "dt", HTMLEL_DLCONTENTS, HTMLEL_INLINE },
+ { 2, "em", HTMLEL_INLINE, 0 },
+ { 2, "li", HTMLEL_LI, HTMLEL_FLOW },
+ { 2, "ol", HTMLEL_BLOCK, HTMLEL_LI },
+ { 1, "p", HTMLEL_BLOCK, HTMLEL_INLINE },
+ { 2, "td", HTMLEL_TRCONTENTS | HTMLEL_AUTOCLOSE, HTMLEL_FLOW },
+ { 2, "th", HTMLEL_TRCONTENTS | HTMLEL_AUTOCLOSE, HTMLEL_FLOW },
+ { 2, "tr", HTMLEL_TABLECONTENTS | HTMLEL_AUTOCLOSE, HTMLEL_TRCONTENTS },
+ { 5, "table", HTMLEL_BLOCK, HTMLEL_TABLECONTENTS },
+ { 2, "ul", HTMLEL_BLOCK, HTMLEL_LI },
+ { 0, 0, 0, 0 }
+};
+#define HTMLELDESC_B (htmleldescs + 1)
+#define HTMLELDESC_BR (htmleldescs + 2)
+
+/***********************************************************************
+ * addcomment : add a comment to the list of comments if it has doxygen syntax
+ *
+ * Enter: tok struct
+ */
+void
+addcomment(struct tok *tok)
+{
+ if (tok->len >= 1 && (tok->start[0] == '!'
+ || (tok->type == TOK_BLOCKCOMMENT && tok->start[0] == '*')
+ || (tok->type == TOK_INLINECOMMENT && tok->start[0] == '/')))
+ {
+ struct comment *comment;
+ comment = memalloc(sizeof(struct comment));
+ comment->text = memalloc(tok->len + 1);
+ memcpy(comment->text, tok->start, tok->len);
+ comment->text[tok->len] = 0;
+ comment->type = tok->type;
+ comment->filename = tok->filename;
+ comment->linenum = tok->linenum;
+ comment->node = 0;
+ comment->back = 0;
+ if (comment->text[1] == '<') {
+ comment->back = 1;
+ if (!lastidentifier) {
+ locerrorexit(comment->filename, comment->linenum,
+ "no identifier to attach doxygen comment to");
+ }
+ comment->node = lastidentifier;
+ }
+ comment->next = comments;
+ comments = comment;
+ }
+}
+
+/***********************************************************************
+ * setcommentnode : set parse node to attach comments to
+ *
+ * Enter: node2 = parse node for identifier
+ */
+void
+setcommentnode(struct node *node2)
+{
+ struct comment *comment = comments;
+ while (comment && !comment->node) {
+ comment->node = node2;
+ comment = comment->next;
+ }
+ lastidentifier = node2;
+}
+
+/***********************************************************************
+ * joininlinecomments : join adjacent inline comments
+ *
+ * Enter: comment = list of comment structs
+ *
+ * Return: new list of comment structs
+ *
+ * This function also discards any single inline comment that does not
+ * refer back.
+ */
+static struct comment *
+joininlinecomments(struct comment *comments)
+{
+ struct comment **pcomment;
+ pcomment = &comments;
+ for (;;) {
+ struct comment *comment;
+ comment = *pcomment;
+ if (!comment)
+ break;
+ if (comment->type != TOK_INLINECOMMENT) {
+ /* Keep block comment as is. */
+ pcomment = &comment->next;
+ } else if (!comment->back && (!comment->next
+ || comment->next->type != TOK_INLINECOMMENT
+ || comment->next->filename != comment->filename
+ || comment->next->linenum != comment->linenum + 1))
+ {
+ /* Discard single // comment that does not refer back. */
+ *pcomment = comment->next;
+
+ } else {
+ /* Find sequence of adjacent // comments (adjacent lines,
+ * referring to same node) and join them. We do this in two
+ * passes, one to count the length of the comment and one
+ * to join. Note that the list is still in reverse order,
+ * so we expect the line number to decrease by 1 each time. */
+ struct comment *newcomment = 0, *comment2;
+ const char *filename = comment->filename;
+ unsigned int linenum = comment->linenum;
+ for (;;) {
+ char *wp = newcomment->text;
+ comment2 = comment;
+ do {
+ unsigned int len = strlen(comment2->text);
+ if (newcomment)
+ memcpy(wp, comment2->text, len);
+ wp += len;
+ linenum--;
+ comment2 = comment2->next;
+ } while (comment2 && comment2->filename == filename
+ && comment2->linenum == linenum
+ && comment2->node == comment->node);
+ /* Finished a pass. */
+ if (newcomment) {
+ *wp = 0;
+ break;
+ }
+ newcomment = memalloc(sizeof(struct comment)
+ + wp - newcomment->text);
+ newcomment->node = comment->node;
+ newcomment->type = comment->type;
+ newcomment->filename = filename;
+ newcomment->linenum = linenum + 1;
+ }
+ /* Replace the scanned comment struct with newcomment in the
+ * list. */
+ newcomment->next = comment2;
+ *pcomment = newcomment;
+ pcomment = &newcomment->next;
+ }
+ }
+ return comments;
+}
+
+/***********************************************************************
+ * outputchildren : call output recursively on children of cnode
+ *
+ * Enter: cnode
+ * indent = indent (nesting) level of parent
+ * indesc = whether already in <description> or other top-level
+ * descriptive element
+ */
+static void
+outputchildren(struct cnode *cnode, unsigned int indent, int indesc)
+{
+ int curindesc = indesc;
+ cnode = cnode->children;
+ while (cnode) {
+ if (curindesc != cnode->funcs->indesc) {
+ assert(!indesc);
+ printf("%*s<%sdescription>\n", indent + 1, "", curindesc ? "/" : "");
+ curindesc = !curindesc;
+ }
+ (*cnode->funcs->output)(cnode, indent + 2);
+ cnode = cnode->next;
+ }
+ if (curindesc != indesc)
+ printf("%*s<%sdescription>\n", indent + 1, "", curindesc ? "/" : "");
+}
+
+/***********************************************************************
+ * default_askend : ask node if it wants to end at a para start (default
+ * implementation)
+ *
+ * Enter: cnode
+ * type = 0 else cnodefuncs for newly starting para
+ *
+ * Return: non-zero if this node wants to end
+ */
+static int
+default_askend(struct cnode *cnode, const struct cnodefuncs *type)
+{
+ return 1;
+}
+
+/***********************************************************************
+ * root_askend : ask root node if it wants to end at a para start
+ *
+ * Enter: cnode for root
+ * type = 0 else cnodefuncs for newly starting para
+ *
+ * Return: non-zero if this node wants to end
+ */
+static int
+root_askend(struct cnode *cnode, const struct cnodefuncs *type)
+{
+ return 0;
+}
+
+/***********************************************************************
+ * root_output : output root cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+root_output(struct cnode *cnode, unsigned int indent)
+{
+ outputchildren(cnode, indent, 0);
+}
+
+/***********************************************************************
+ * cnode type root
+ */
+static const struct cnodefuncs root_funcs = {
+ 0, /* !indesc */
+ 1, /* needpara */
+ &root_askend,
+ 0, /* end */
+ &root_output,
+};
+
+/***********************************************************************
+ * endcnode : end current cnode
+ *
+ * Enter: cnode = current cnode
+ *
+ * Return: cnode = new current code (parent of old one)
+ */
+static struct cnode *
+endcnode(struct cnode *cnode)
+{
+ if (cnode->funcs->end)
+ (*cnode->funcs->end)(cnode);
+ /* Reverse the children list. */
+ {
+ struct cnode *child = cnode->children;
+ cnode->children = 0;
+ while (child) {
+ struct cnode *next = child->next;
+ child->next = cnode->children;
+ cnode->children = child;
+ child = next;
+ }
+ }
+ return cnode->parent;
+}
+
+/***********************************************************************
+ * endspecificcnode : end a specific type of cnode
+ *
+ * Enter: cnode = current cnode
+ * type = type of node to end
+ * filename, linenum = filename and line number (for error reporting)
+ *
+ * Return: new current cnode
+ */
+static struct cnode *
+endspecificcnode(struct cnode *cnode, const struct cnodefuncs *type,
+ const char *filename, unsigned int linenum)
+{
+ while (cnode->funcs != type) {
+ if (cnode->funcs == &root_funcs)
+ locerrorexit(filename, linenum, "unmatched \\endcode");
+ cnode = endcnode(cnode);
+ }
+ return cnode;
+}
+
+/***********************************************************************
+ * startcnode : start a newly created cnode
+ *
+ * Enter: cnode = current cnode
+ * newcnode = new cnode to start
+ *
+ * Return: new current cnode (which is the same as newcnode)
+ */
+static struct cnode *
+startcnode(struct cnode *cnode, struct cnode *newcnode)
+{
+ newcnode->parent = cnode;
+ newcnode->next = cnode->children;
+ cnode->children = newcnode;
+ return newcnode;
+}
+
+/***********************************************************************
+ * para_output : output para cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+para_output(struct cnode *cnode, unsigned int indent)
+{
+ printf("%*s<p>\n", indent, "");
+ outputchildren(cnode, indent, 1);
+ printf("%*s</p>\n", indent, "");
+}
+
+/***********************************************************************
+ * para_end : end a para cnode
+ *
+ * Enter: cnode struct
+ */
+static void
+para_end(struct cnode *cnode)
+{
+ /* If the para cnode is empty, remove it. */
+ if (!cnode->children)
+ cnode->parent->children = cnode->next;
+}
+
+/***********************************************************************
+ * cnode type para
+ */
+static const struct cnodefuncs para_funcs = {
+ 1, /* indesc */
+ 0, /* !needpara */
+ &default_askend,
+ &para_end, /* end */
+ &para_output,
+};
+
+/***********************************************************************
+ * brief_output : output brief cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+brief_output(struct cnode *cnode, unsigned int indent)
+{
+ printf("%*s<brief>\n", indent, "");
+ outputchildren(cnode, indent, 1);
+ printf("%*s</brief>\n", indent, "");
+}
+
+/***********************************************************************
+ * cnode type brief
+ */
+static const struct cnodefuncs brief_funcs = {
+ 0, /* !indesc */
+ 0, /* !needpara */
+ &default_askend,
+ 0, /* end */
+ &brief_output,
+};
+
+/***********************************************************************
+ * return_output : output return cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+return_output(struct cnode *cnode, unsigned int indent)
+{
+ printf("%*s<description><p>\n", indent, "");
+ outputchildren(cnode, indent, 1);
+ printf("%*s</p></description>\n", indent, "");
+}
+
+/***********************************************************************
+ * cnode type return
+ */
+static const struct cnodefuncs return_funcs = {
+ 0, /* !indesc */
+ 0, /* !needpara */
+ &default_askend,
+ 0, /* end */
+ &return_output,
+};
+
+/***********************************************************************
+ * author_output : output name cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+name_output(struct cnode *cnode, unsigned int indent)
+{
+ printf("%*s<name>\n", indent, "");
+ outputchildren(cnode, indent, 1);
+ printf("%*s</name>\n", indent, "");
+}
+
+/***********************************************************************
+ * cnode type name
+ */
+static const struct cnodefuncs name_funcs = {
+ 0, /* !indesc */
+ 0, /* !needpara */
+ &default_askend,
+ 0, /* end */
+ &name_output,
+};
+
+/***********************************************************************
+ * author_output : output author cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+author_output(struct cnode *cnode, unsigned int indent)
+{
+ printf("%*s<author>\n", indent, "");
+ outputchildren(cnode, indent, 1);
+ printf("%*s</author>\n", indent, "");
+}
+
+/***********************************************************************
+ * cnode type author
+ */
+static const struct cnodefuncs author_funcs = {
+ 0, /* !indesc */
+ 0, /* !needpara */
+ &default_askend,
+ 0, /* end */
+ &author_output,
+};
+
+/***********************************************************************
+ * version_output : output version cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+version_output(struct cnode *cnode, unsigned int indent)
+{
+ printf("%*s<version>\n", indent, "");
+ outputchildren(cnode, indent, 1);
+ printf("%*s</version>\n", indent, "");
+}
+
+/***********************************************************************
+ * cnode type version
+ */
+static const struct cnodefuncs version_funcs = {
+ 0, /* !indesc */
+ 0, /* !needpara */
+ &default_askend,
+ 0, /* end */
+ &version_output,
+};
+
+/***********************************************************************
+ * cnode type code
+ */
+/***********************************************************************
+ * code_end : end a code cnode
+ *
+ * Enter: cnode struct
+ */
+static void
+code_end(struct cnode *cnode)
+{
+ if (incode) {
+ /* The incode flag has not been cleared, so this code cnode is
+ * being ended implicitly. We complain about that. */
+ locerrorexit(cnode->filename, cnode->linenum, "mismatched \\code");
+ }
+}
+
+/***********************************************************************
+ * code_output : output code cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+code_output(struct cnode *cnode, unsigned int indent)
+{
+ /* Note capitalization to differentiate it from HTML code element. */
+ if(cnode->attrtext)
+ printf("%*s<Code %s>", indent, "", cnode->attrtext);
+ else
+ printf("%*s<Code>", indent, "");
+ outputchildren(cnode, indent, 1);
+ printf("</Code>\n");
+}
+
+static const struct cnodefuncs code_funcs = {
+ 0, /* indesc */
+ 0, /* !needpara */
+ &default_askend,
+ &code_end, /* end */
+ &code_output,
+};
+
+/***********************************************************************
+ * startpara : start a new para cnode in the parse tree
+ *
+ * Enter: cnode = current cnode
+ * type = vtable for particular type of cnode
+ *
+ * Return: new current cnode
+ */
+static struct cnode *
+startpara(struct cnode *cnode, const struct cnodefuncs *type)
+{
+ struct cnode *newcnode;
+ while ((*cnode->funcs->askend)(cnode, type))
+ cnode = endcnode(cnode);
+ newcnode = memalloc(sizeof(struct cnode));
+ newcnode->funcs = type;
+ return startcnode(cnode, newcnode);
+}
+
+/***********************************************************************
+ * cnode type text
+ */
+struct textcnode {
+ struct cnode cn;
+ unsigned char *data;
+ unsigned int len;
+ unsigned int max;
+};
+
+/***********************************************************************
+ * text_end : end a text cnode
+ *
+ * Enter: cnode struct
+ */
+static void
+text_end(struct cnode *cnode)
+{
+ struct textcnode *textcnode = (void *)cnode;
+ textcnode->data[textcnode->len] = 0;
+ textcnode->max = textcnode->len + 1;
+ textcnode->data = memrealloc(textcnode->data, textcnode->max);
+}
+
+/***********************************************************************
+ * text_output : output text cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+text_output(struct cnode *cnode, unsigned int indent)
+{
+ /* We do not indent, in case this is inside a code cnode. */
+ struct textcnode *textcnode = (void *)cnode;
+ unsigned int len = textcnode->len;
+ unsigned const char *p = textcnode->data;
+ while (len) {
+ unsigned int thislen;
+ const char *thisptr;
+ thislen = p[0];
+ /* (void *) cast is to avoid a warning from the MS compiler.
+ * I think the warning is wrong, but I could be wrong. */
+ memcpy((void *)&thisptr, p + 1, sizeof(void *));
+ p += 1 + sizeof(void *);
+ len -= 1 + sizeof(void *);
+ printtext(thisptr, thislen, 0);
+ }
+}
+
+static const struct cnodefuncs text_funcs = {
+ 1, /* !indesc */
+ 0, /* !needpara */
+ &default_askend,
+ &text_end, /* end */
+ &text_output,
+};
+
+/***********************************************************************
+ * cnode type html (HTML element)
+ */
+struct htmlcnode {
+ struct cnode cn;
+ const struct htmleldesc *desc;
+ char attrs[1];
+};
+
+/***********************************************************************
+ * html_end : end an html cnode
+ *
+ * Enter: cnode struct
+ */
+static void
+html_end(struct cnode *cnode)
+{
+ if (((struct htmlcnode *)cnode)->desc->flags & HTMLEL_BLOCK)
+ inhtmlblock--;
+}
+
+/***********************************************************************
+ * html_output : output html cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+html_output(struct cnode *cnode, unsigned int indent)
+{
+ struct htmlcnode *htmlcnode = (void *)cnode;
+ if (!(htmlcnode->desc->flags & HTMLEL_INLINE))
+ printf("%*s", indent, "");
+ if (htmlcnode->cn.children) {
+ printf("<%s%s>", htmlcnode->desc->name, htmlcnode->attrs);
+ if (!(htmlcnode->desc->flags & HTMLEL_INLINE))
+ putchar('\n');
+ outputchildren(&htmlcnode->cn, indent, 1);
+ if (!(htmlcnode->desc->flags & HTMLEL_INLINE))
+ printf("%*s", indent, "");
+ printf("</%s>", htmlcnode->desc->name);
+ } else
+ printf("<%s%s/>", htmlcnode->desc->name, htmlcnode->attrs);
+ if (!(htmlcnode->desc->flags & HTMLEL_INLINE))
+ putchar('\n');
+}
+
+static const struct cnodefuncs html_funcs = {
+ 1, /* indesc */
+ 0, /* !needpara */
+ &default_askend,
+ &html_end, /* end */
+ &html_output,
+};
+
+/***********************************************************************
+ * starthtmlcnode : start a new html cnode
+ *
+ * Enter: cnode = current cnode
+ * htmleldesc = html element descriptor
+ * attrs = attributes text
+ * attrslen = length of attributes text
+ * filename
+ * linenum = line number
+ *
+ * Return: new current cnode
+ */
+static struct cnode *
+starthtmlcnode(struct cnode *cnode, const struct htmleldesc *htmleldesc,
+ const char *attrs, unsigned int attrslen,
+ const char *filename, unsigned int linenum)
+{
+ struct htmlcnode *htmlcnode;
+ /* First close enough elements to get to a content
+ * model that will accept this new element. */
+ for (;;) {
+ if (cnode->funcs != &html_funcs) {
+ /* Not in any html element. We can accept any block element
+ * (in which case we need to close the current paragraph
+ * first) or any inline element (in which case we need to
+ * close the current text cnode first). */
+ if (!(htmleldesc->flags & HTMLEL_INLINE)) {
+ if (!(htmleldesc->flags & HTMLEL_BLOCK))
+ locerrorexit(filename, linenum, "<%s> not valid here", htmleldesc->name);
+ while ((*cnode->funcs->askend)(cnode, 0))
+ cnode = endcnode(cnode);
+ } else {
+ while (cnode->funcs == &text_funcs)
+ cnode = endcnode(cnode);
+ }
+ break;
+ }
+ htmlcnode = (struct htmlcnode *)cnode;
+ if (!(htmleldesc->flags & htmlcnode->desc->content))
+ locerrorexit(filename, linenum, "<%s> not valid here", htmleldesc->name);
+ break;
+ }
+ if (htmleldesc->flags & HTMLEL_BLOCK)
+ inhtmlblock++;
+ /* Create the new html cnode. */
+ htmlcnode = memalloc(sizeof(struct htmlcnode) + attrslen);
+ htmlcnode->desc = htmleldesc;
+ htmlcnode->cn.funcs = &html_funcs;
+ htmlcnode->cn.filename = filename;
+ htmlcnode->cn.linenum = linenum;
+ memcpy(htmlcnode->attrs, attrs, attrslen);
+ htmlcnode->attrs[attrslen] = 0;
+ /* Start the html cnode. */
+ cnode = startcnode(cnode, &htmlcnode->cn);
+ return cnode;
+}
+
+/***********************************************************************
+ * param_output : output param cnode
+ *
+ * Enter: cnode for param
+ * indent = indent (nesting) level
+ *
+ * This is only used for a \param inside a \def-device-cap. A normal
+ * \param that gets attached to a function argument gets changed to
+ * a \return so it does not use this output function.
+ */
+static void
+param_output(struct cnode *cnode, unsigned int indent)
+{
+ struct paramcnode *paramcnode = (void *)cnode;
+ printf("%*s<param identifier=\"%s\">\n", indent, "", paramcnode->name);
+ outputchildren(&paramcnode->cn, indent, 1);
+ printf("%*s</param>\n", indent, "");
+}
+
+/***********************************************************************
+ * cnode type param
+ */
+static const struct cnodefuncs param_funcs = {
+ 0, /* !indesc */
+ 0, /* !needpara */
+ &default_askend,
+ 0, /* end */
+ &param_output,
+};
+
+/***********************************************************************
+ * cnode type throw
+ */
+static const struct cnodefuncs throw_funcs = {
+ 0, /* !indesc */
+ 0, /* !needpara */
+ &default_askend,
+ 0, /* end */
+ &return_output,
+};
+
+/***********************************************************************
+ * startparamcnode : start param (or throw) cnode
+ *
+ * Enter: cnode = current cnode
+ * word = name of param
+ * wordlen = length of name
+ * inout = bit 0 = in, bit 1 = out
+ * type = &param_funcs or &throw_funcs
+ *
+ * Return: new current cnode
+ */
+static struct cnode *
+startparamcnode(struct cnode *cnode, const char *word, unsigned int wordlen,
+ int inout, const struct cnodefuncs *funcs)
+{
+ struct paramcnode *paramcnode;
+ paramcnode = memalloc(sizeof(struct paramcnode) + wordlen);
+ paramcnode->cn.funcs = funcs;
+ memcpy(paramcnode->name, word, wordlen);
+ paramcnode->name[wordlen] = 0;
+ paramcnode->inout = inout;
+ return startcnode(cnode, &paramcnode->cn);
+}
+
+/***********************************************************************
+ * api_feature_output : output api-feature cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+api_feature_output(struct cnode *cnode, unsigned int indent)
+{
+ struct paramcnode *paramcnode = (void *)cnode;
+ printf("%*s<api-feature identifier=\"%s\">\n", indent, "", paramcnode->name);
+ outputchildren(cnode, indent, 1);
+ printf("%*s</api-feature>\n", indent, "");
+}
+
+/***********************************************************************
+ * cnode type api_feature
+ */
+static const struct cnodefuncs api_feature_funcs = {
+ 0, /* !indesc */
+ 0, /* needpara */
+ &default_askend,
+ 0, /* end */
+ &api_feature_output,
+};
+
+/***********************************************************************
+ * device_cap_output : output device-cap cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+device_cap_output(struct cnode *cnode, unsigned int indent)
+{
+ struct paramcnode *paramcnode = (void *)cnode;
+ printf("%*s<device-cap identifier=\"%s\">\n", indent, "", paramcnode->name);
+ outputchildren(cnode, indent, 1);
+ printf("%*s</device-cap>\n", indent, "");
+}
+
+/***********************************************************************
+ * cnode type device_cap
+ */
+static const struct cnodefuncs device_cap_funcs = {
+ 0, /* !indesc */
+ 0, /* needpara */
+ &default_askend,
+ 0, /* end */
+ &device_cap_output,
+};
+
+/***********************************************************************
+ * def_api_feature_askend : ask if def-api-feature cnode wants to end at new para
+ *
+ * Enter: cnode for def-api-feature
+ * type = cnodefuncs for new para (0 if html block element)
+ *
+ * Return: non-zero to end the def-api-feature
+ */
+static int
+def_api_feature_askend(struct cnode *cnode, const struct cnodefuncs *type)
+{
+ /* A def-api-feature does not end at a plain para, an html block element,
+ * a brief para, or a device-cap. */
+ if (!type || type == &para_funcs || type == &device_cap_funcs || type == &brief_funcs)
+ return 0;
+ return 1;
+}
+
+/***********************************************************************
+ * def_api_feature_output : output def-api-feature cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+def_api_feature_output(struct cnode *cnode, unsigned int indent)
+{
+ struct paramcnode *paramcnode = (void *)cnode;
+ printf("%*s<def-api-feature identifier=\"%s\">\n", indent, "", paramcnode->name);
+ printf("%*s<descriptive>\n", indent + 2, "");
+ outputchildren(cnode, indent + 2, 0);
+ printf("%*s</descriptive>\n", indent + 2, "");
+ printf("%*s</def-api-feature>\n", indent, "");
+}
+
+/***********************************************************************
+ * cnode type def_api_feature
+ */
+static const struct cnodefuncs def_api_feature_funcs = {
+ 0, /* !indesc */
+ 1, /* needpara */
+ &def_api_feature_askend,
+ 0, /* end */
+ &def_api_feature_output,
+};
+
+/***********************************************************************
+ * def_api_feature_set_askend : ask if def-api-feature-set cnode wants to end at new para
+ *
+ * Enter: cnode for def-api-feature-set
+ * type = cnodefuncs for new para (0 if html block element)
+ *
+ * Return: non-zero to end the def-api-feature-set
+ */
+static int
+def_api_feature_set_askend(struct cnode *cnode, const struct cnodefuncs *type)
+{
+ /* A def-api-feature-set does not end at a plain para, an html block element,
+ * a brief para, or an api-feature. */
+ if (!type || type == &para_funcs || type == &api_feature_funcs || type == &brief_funcs)
+ return 0;
+ return 1;
+}
+
+/***********************************************************************
+ * def_api_feature_set_output : output def-api-feature-set cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+def_api_feature_set_output(struct cnode *cnode, unsigned int indent)
+{
+ struct paramcnode *paramcnode = (void *)cnode;
+ printf("%*s<def-api-feature-set identifier=\"%s\">\n", indent, "", paramcnode->name);
+ printf("%*s<descriptive>\n", indent + 2, "");
+ outputchildren(cnode, indent + 2, 0);
+ printf("%*s</descriptive>\n", indent + 2, "");
+ printf("%*s</def-api-feature-set>\n", indent, "");
+}
+
+/***********************************************************************
+ * cnode type def_api_feature_set
+ */
+static const struct cnodefuncs def_api_feature_set_funcs = {
+ 0, /* !indesc */
+ 1, /* needpara */
+ &def_api_feature_set_askend,
+ 0, /* end */
+ &def_api_feature_set_output,
+};
+
+/***********************************************************************
+ * def_instantiated_askend : ask if def-instantiated cnode wants to end at new para
+ *
+ * Enter: cnode for def-instantiated
+ * type = cnodefuncs for new para (0 if html block element)
+ *
+ * Return: non-zero to end the def-instantiated
+ */
+static int
+def_instantiated_askend(struct cnode *cnode, const struct cnodefuncs *type)
+{
+ /* A def-instantiated does not end at a plain para, an html block element,
+ * a brief para, or an api-feature. */
+ if (!type || type == &para_funcs || type == &api_feature_funcs || type == &brief_funcs)
+ return 0;
+ return 1;
+}
+
+/***********************************************************************
+ * def_instantiated_output : output def-instantiated cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+def_instantiated_output(struct cnode *cnode, unsigned int indent)
+{
+ printf("%*s<def-instantiated>\n", indent, "");
+ printf("%*s<descriptive>\n", indent + 2, "");
+ outputchildren(cnode, indent + 2, 0);
+ printf("%*s</descriptive>\n", indent + 2, "");
+ printf("%*s</def-instantiated>\n", indent, "");
+}
+
+/***********************************************************************
+ * cnode type def_instantiated
+ */
+static const struct cnodefuncs def_instantiated_funcs = {
+ 0, /* !indesc */
+ 1, /* needpara */
+ &def_instantiated_askend,
+ 0, /* end */
+ &def_instantiated_output,
+};
+
+/***********************************************************************
+ * def_device_cap_askend : ask if def-device-cap cnode wants to end at new para
+ *
+ * Enter: cnode for def-device-cap
+ * type = cnodefuncs for new para (0 if html block element)
+ *
+ * Return: non-zero to end the def-device-cap
+ */
+static int
+def_device_cap_askend(struct cnode *cnode, const struct cnodefuncs *type)
+{
+ /* A def-device-cap does not end at a plain para, an html block element,
+ * a \brief para, or a param. */
+ if (!type || type == &para_funcs || type == &param_funcs || type == &brief_funcs)
+ return 0;
+ return 1;
+}
+
+/***********************************************************************
+ * def_device_cap_output : output def_device-cap cnode
+ *
+ * Enter: cnode for root
+ * indent = indent (nesting) level
+ */
+static void
+def_device_cap_output(struct cnode *cnode, unsigned int indent)
+{
+ struct paramcnode *paramcnode = (void *)cnode;
+ printf("%*s<def-device-cap identifier=\"%s\">\n", indent, "", paramcnode->name);
+ printf("%*s<descriptive>\n", indent + 2, "");
+ outputchildren(cnode, indent + 2, 0);
+ printf("%*s</descriptive>\n", indent + 2, "");
+ printf("%*s</def-device-cap>\n", indent, "");
+}
+
+/***********************************************************************
+ * cnode type def_device_cap
+ */
+static const struct cnodefuncs def_device_cap_funcs = {
+ 0, /* !indesc */
+ 1, /* needpara */
+ &def_device_cap_askend,
+ 0, /* end */
+ &def_device_cap_output,
+};
+
+/***********************************************************************
+ * addtext : add text to current text node, starting one if necessary
+ *
+ * Enter: cnode = current cnode
+ * text
+ * len = length of text
+ *
+ * Return: new current cnode
+ */
+static struct cnode *
+addtext(struct cnode *cnode, const char *text, unsigned int len)
+{
+ struct textcnode *textcnode;
+ if (!len)
+ return cnode;
+ if (cnode->funcs != &text_funcs) {
+ /* Start new text cnode. */
+ textcnode = memalloc(sizeof(struct textcnode));
+ textcnode->cn.funcs = &text_funcs;
+ cnode = startcnode(cnode, &textcnode->cn);
+ }
+ textcnode = (void *)cnode;
+ do {
+ unsigned char buf[1 + sizeof(void *)];
+ unsigned int thislen = len;
+ if (thislen > 255)
+ thislen = 255;
+ /* Encode a record as a single byte length followed by a pointer. */
+ buf[0] = thislen;
+ memcpy(buf + 1, &text, sizeof(void *));
+ /* Add to the text cnode's data. */
+ if (textcnode->len + sizeof(buf) >= textcnode->max) {
+ /* Need to reallocate (or allocate) data buffer. */
+ textcnode->max = textcnode->max ? 2 * textcnode->max : 1024;
+ textcnode->data = memrealloc(textcnode->data, textcnode->max);
+ }
+ memcpy(textcnode->data + textcnode->len, buf, sizeof(buf));
+ textcnode->len += sizeof(buf);
+ text += thislen;
+ len -= thislen;
+ } while (len);
+ return &textcnode->cn;
+}
+
+/***********************************************************************
+ * iswhitespace : determine if character is whitespace
+ *
+ * Enter: ch = character
+ *
+ * Return: 1 if whitespace
+ */
+static inline int
+iswhitespace(int ch)
+{
+ unsigned int i = ch - 1;
+ if (i >= 32)
+ return 0;
+ return 0x80001100 >> i & 1;
+}
+
+/***********************************************************************
+ * parseword : parse the next word, ignoring leading whitespace
+ *
+ * Enter: *pp = text pointer
+ *
+ * Return: 0 else start of word (*pp updated to just beyond end)
+ */
+static const char *
+parseword(const char **pp)
+{
+ const char *p = *pp, *word = 0;
+ int ch = *p;
+ while (iswhitespace(ch))
+ ch = *++p;
+ word = p;
+ while ((unsigned)((ch & ~0x20) - 'A') <= 'Z' - 'A'
+ || (unsigned)(ch - '0') < 10 || ch == '_' || ch == '.'
+ || ch == ':' || ch == '/' || ch == '-')
+ {
+ ch = *++p;
+ }
+ if (p == word)
+ return 0;
+ *pp = p;
+ return word;
+}
+
+/***********************************************************************
+ * Doxygen command handlers
+ *
+ * Enter: p = text just after command
+ * *pcnode = pointer to current cnode struct
+ * type = 0 else cnodefuncs pointer for type of node to start
+ * filename, linenum = current filename and line number (for error reporting)
+ *
+ * Return: p = updated if extra text was eaten
+ *
+ * On return, *pnode is updated if any node was closed or opened.
+ */
+
+/***********************************************************************
+ * Doxygen command handler : \b
+ */
+static const char *
+dox_b(const char *p, struct cnode **pcnode, const struct cnodefuncs *type,
+ const char *filename, unsigned int linenum, const char *cmdname)
+{
+ struct cnode *cnode = *pcnode;
+ const char *word = parseword(&p);
+ /* Silently ignore \b with no following word. */
+ if (word) {
+ struct cnode *mycnode;
+ mycnode = cnode = starthtmlcnode(cnode, HTMLELDESC_B, 0, 0, filename, linenum);
+ cnode = addtext(cnode, word, p - word);
+ while (cnode != mycnode)
+ cnode = endcnode(cnode);
+ cnode = endcnode(cnode);
+ }
+ *pcnode = cnode;
+ return p;
+}
+
+/***********************************************************************
+ * Doxygen command handler : \n
+ */
+static const char *
+dox_n(const char *p, struct cnode **pcnode, const struct cnodefuncs *type,
+ const char *filename, unsigned int linenum, const char *cmdname)
+{
+ struct cnode *cnode = *pcnode;
+ cnode = starthtmlcnode(cnode, HTMLELDESC_BR, 0, 0, filename, linenum);
+ cnode = endcnode(cnode);
+ *pcnode = cnode;
+ return p;
+}
+
+/***********************************************************************
+ * Doxygen command handler : \code
+ */
+static const char *
+dox_code(const char *p, struct cnode **pcnode, const struct cnodefuncs *type,
+ const char *filename, unsigned int linenum, const char *cmdname)
+{
+ *pcnode = startpara(*pcnode, &code_funcs);
+ (*pcnode)->filename = filename;
+ (*pcnode)->linenum = linenum; /* for reporting mismatched \code error */
+ incode = 1;
+ return p;
+}
+
+/***********************************************************************
+ * Doxygen command handler : \endcode
+ */
+static const char *
+dox_endcode(const char *p, struct cnode **pcnode, const struct cnodefuncs *type,
+ const char *filename, unsigned int linenum, const char *cmdname)
+{
+ incode = 0;
+ *pcnode = endspecificcnode(*pcnode, &code_funcs, filename, linenum);
+ return p;
+}
+
+/***********************************************************************
+ * Doxygen command handler : \param
+ */
+static const char *
+dox_param(const char *p, struct cnode **pcnode, const struct cnodefuncs *type,
+ const char *filename, unsigned int linenum, const char *cmdname)
+{
+ struct cnode *cnode = *pcnode;
+ unsigned int inout = 0;
+ const char *word;
+ /* Check for "in", "out" or both as attributes. */
+ if (*p == '[') {
+ for (;;) {
+ p++;
+ if (!memcmp(p, "in", 2)) {
+ inout |= 1;
+ p += 2;
+ } else if (!memcmp(p, "out", 3)) {
+ inout |= 2;
+ p += 3;
+ } else
+ break;
+ if (*p != ',')
+ break;
+ }
+ if (*p != ']')
+ locerrorexit(filename, linenum, "bad attributes on \\param");
+ p++;
+ }
+ /* Get the next word as the parameter name. */
+ word = parseword(&p);
+ if (!word)
+ locerrorexit(filename, linenum, "expected word after \\param");
+ /* Close any open nodes. */
+ while ((*cnode->funcs->askend)(cnode, type))
+ cnode = endcnode(cnode);
+ /* Create a new param cnode. */
+ cnode = startparamcnode(cnode, word, p - word, inout, type);
+ cnode->filename = filename;
+ cnode->linenum = linenum;
+ *pcnode = cnode;
+ return p;
+}
+
+/***********************************************************************
+ * Doxygen command handler : \brief, \return
+ */
+static const char *
+dox_para(const char *p, struct cnode **pcnode, const struct cnodefuncs *type,
+ const char *filename, unsigned int linenum, const char *cmdname)
+{
+ *pcnode = startpara(*pcnode, type);
+ return p;
+}
+
+/***********************************************************************
+ * Doxygen command handler : \throw
+ */
+static const char *
+dox_throw(const char *p, struct cnode **pcnode, const struct cnodefuncs *type,
+ const char *filename, unsigned int linenum, const char *cmdname)
+{
+ struct cnode *cnode = *pcnode;
+ const char *word;
+ /* Get the next word as the exception name. */
+ word = parseword(&p);
+ if (!word)
+ locerrorexit(filename, linenum, "expected word after \\throw");
+ /* Close any open nodes. */
+ while ((*cnode->funcs->askend)(cnode, type))
+ cnode = endcnode(cnode);
+ /* Create a new throw cnode. */
+ cnode = startparamcnode(cnode, word, p - word, 0, type);
+ cnode->filename = filename;
+ cnode->linenum = linenum;
+ *pcnode = cnode;
+ return p;
+}
+
+/***********************************************************************
+ * Doxygen command handler : \lang
+ */
+static const char *
+dox_attr(const char *p, struct cnode **pcnode, const struct cnodefuncs *type,
+ const char *filename, unsigned int linenum, const char *cmdname)
+{
+ struct cnode *cnode = *pcnode;
+ const char *word;
+ int len, wordlen, offset = 0;
+ char *attrtext;
+ /* Get the next word as the attribute value. */
+ word = parseword(&p);
+ if (!word)
+ locerrorexit(filename, linenum, "expected word after \\%s", cmdname);
+
+ len = strlen(cmdname) + (wordlen = p-word) + 4; /* p="word"\0 */
+ if(cnode->attrtext)
+ len += (offset = strlen(cnode->attrtext)) + 1; /* add space for space */
+ attrtext = memalloc(len);
+ if(offset) {
+ memcpy(attrtext, cnode->attrtext, offset);
+ attrtext[offset++] = ' ';
+ memfree(((void*)cnode->attrtext));
+ }
+ offset += sprintf(&attrtext[offset], "%s=\"", cmdname);
+ memcpy(&attrtext[offset], word, wordlen);
+ strcpy(&attrtext[offset + wordlen], "\"");
+ cnode->attrtext = attrtext;
+ /* skip delimiter because it won't be done otherwise */
+ if(incode && iswhitespace(*p)) ++p;
+ return p;
+}
+
+/***********************************************************************
+ * commands : table of Doxygen commands
+ */
+struct command {
+ const char *(*func)(const char *p, struct cnode **pcnode, const struct cnodefuncs *type, const char *filename, unsigned int linenum, const char *cmdname);
+ const struct cnodefuncs *type;
+ unsigned int namelen;
+ const char *name;
+};
+static const struct command commands[] = {
+ { &dox_throw, &def_api_feature_funcs, 15, "def-api-feature" },
+ { &dox_throw, &def_api_feature_set_funcs, 19, "def-api-feature-set" },
+ { &dox_para, &def_instantiated_funcs, 16, "def-instantiated" },
+ { &dox_para, &name_funcs, 4, "name" },
+ { &dox_para, &author_funcs, 6, "author" },
+ { &dox_b, 0, 1, "b" },
+ { &dox_para, &brief_funcs, 5, "brief" },
+ { &dox_code, 0, 4, "code" },
+ { &dox_throw, &def_device_cap_funcs, 14, "def-device-cap" },
+ { &dox_attr, 0, 4, "lang" },
+ { &dox_endcode, 0, 7, "endcode" },
+ { &dox_n, 0, 1, "n" },
+ { &dox_param, &param_funcs, 5, "param" },
+ { &dox_para, &return_funcs, 6, "return" },
+ { &dox_throw, &throw_funcs, 5, "throw" },
+ { &dox_throw, &api_feature_funcs, 11, "api-feature" },
+ { &dox_throw, &device_cap_funcs, 10, "device-cap" },
+ { &dox_para, &version_funcs, 7, "version" },
+ { 0, 0, 0 }
+};
+
+/***********************************************************************
+ * parsehtmltag : parse html tag
+ *
+ * Enter: start = start of tag, the '<' char
+ * *pcnode = current cnode
+ * filename = filename
+ * *plinenum = current line number
+ *
+ * Return: just after the tag
+ * *pcnode and *plinenum updated if applicable
+ */
+static const char *
+parsehtmltag(const char *start, struct cnode **pcnode,
+ const char *filename, unsigned int *plinenum)
+{
+ struct cnode *cnode = *pcnode;
+ const char *end = start + 1, *endname = 0, *name = end;
+ int ch = *end;
+ int quote = 0;
+ int close = 0;
+ unsigned int linenum = *plinenum;
+ const struct htmleldesc *htmleldesc;
+ if (ch == '/') {
+ close = 1;
+ ch = *++end;
+ name = end;
+ }
+ /* Find the end of the tag. */
+ for (;;) {
+ if (!ch)
+ locerrorexit(filename, *plinenum, "unterminated HTML tag");
+ if (ch == '\n')
+ linenum++;
+ else if (iswhitespace(ch) || ch == '/') {
+ if (!endname)
+ endname = end;
+ } else if (!quote) {
+ if (ch == '"' || ch == '\'')
+ quote = ch;
+ else if (ch == '>')
+ break;
+ } else {
+ if (ch == quote)
+ quote = 0;
+ }
+ ch = *++end;
+ }
+ if (!endname)
+ endname = end;
+ end++;
+ /* See if it's an xml open-close tag. */
+ if (!close && endname != name && end[-2] == '/')
+ close = 2;
+ /* Find the tag from our list. */
+ htmleldesc = htmleldescs;
+ for (;;) {
+ if (!htmleldesc->namelen) {
+ locerrorexit(filename, *plinenum, "unrecognized HTML tag %.*s",
+ end - start, start);
+ }
+ if (htmleldesc->namelen == endname - name
+ && !strncasecmp(htmleldesc->name, name, endname - name))
+ {
+ break;
+ }
+ htmleldesc++;
+ }
+ if (close == 1) {
+ /* Closing tag. Find open element to close. */
+ for (;;) {
+ struct htmlcnode *htmlcnode;
+ if (cnode->funcs != &text_funcs) {
+ if (cnode->funcs != &html_funcs) {
+ locerrorexit(filename, *plinenum, "mismatched %.*s",
+ end - start, start);
+ }
+ htmlcnode = (struct htmlcnode *)cnode;
+ if (htmlcnode->desc == htmleldesc)
+ break;
+ if (!(htmlcnode->desc->flags & HTMLEL_AUTOCLOSE)) {
+ locerrorexit(filename, htmlcnode->cn.linenum,
+ "mismatched <%.*s>",
+ htmlcnode->desc->namelen, htmlcnode->desc->name);
+ }
+ }
+ cnode = endcnode(cnode);
+ }
+ cnode = endcnode(cnode);
+ } else {
+ /* Opening tag. */
+ if (close !=2)
+ cnode = starthtmlcnode(cnode, htmleldesc, endname, end - 1 - endname, filename, *plinenum);
+ else // don't include the closing "/" in the attributes list
+ cnode = starthtmlcnode(cnode, htmleldesc, endname, end - 2 - endname, filename, *plinenum);
+ if (close == 2 || (htmleldesc->content & HTMLEL_EMPTY)) {
+ /* Empty element -- close it again. */
+ cnode = endcnode(cnode);
+ }
+ }
+ *pcnode = cnode;
+ *plinenum = linenum;
+ return end;
+}
+
+/***********************************************************************
+ * parsecomment : parse one comment
+ *
+ * Enter: comment struct
+ */
+static void
+parsecomment(struct comment *comment)
+{
+ struct cnode *cnode = &comment->root;
+ const char *p = comment->text + comment->back;
+ unsigned int linenum = comment->linenum - 1;
+ int ch;
+ curcomment = comment;
+ incode = 0;
+ inhtmlblock = 0;
+ cnode->funcs = &root_funcs;
+ for (;;) {
+ /* Start of new line. */
+ const char *starttext;
+ ch = *p;
+ linenum++;
+ {
+ /* Find first non-whitespace character. */
+ const char *p2 = p;
+ int ch2 = ch;
+ while (iswhitespace(ch2))
+ ch2 = *++p2;
+ if (comment->type == TOK_BLOCKCOMMENT && ch2 == '*') {
+ /* Ignore initial * in block comment (even in \code block). */
+ ch2 = *++p2;
+ ch = ch2;
+ p = p2;
+ if (ch == '*')
+ goto checkforlineofstars;
+ while (iswhitespace(ch2))
+ ch2 = *++p2;
+ }
+ if (comment->type == TOK_INLINECOMMENT && ch2 == '/') {
+checkforlineofstars:
+ if (!incode) {
+ /* Ignore whole line of * for block comment or / for inline
+ * comment if that is the only thing on the line. */
+ const char *p3 = p2;
+ int ch3;
+ do ch3 = *++p3; while (ch3 == ch2);
+ while (iswhitespace(ch3)) ch3 = *++p3;
+ if (!ch3 || ch3 == '\n') {
+ /* Reached end of line (or whole comment) -- treat as
+ * empty line. */
+ ch2 = ch3;
+ p2 = p3;
+ }
+ }
+ }
+ if (!incode) {
+ /* Only allow whitespace omission above to take effect if
+ * not in \code block. */
+ ch = ch2;
+ p = p2;
+ }
+ }
+ if (!ch) {
+ /* End of comments -- finish. */
+ break;
+ }
+ if (!incode && !inhtmlblock && ch == '\n') {
+ /* Blank line -- finish any para, but only if not in code and
+ * not in any HTML block element. */
+ while ((*cnode->funcs->askend)(cnode, 0))
+ cnode = endcnode(cnode);
+ p++;
+ continue;
+ }
+ /* Start new para if there isn't already one going. */
+ if (cnode->funcs->needpara)
+ cnode = startpara(cnode, &para_funcs);
+ /* Process text on the line. */
+ starttext = p;
+ while (ch && ch != '\n') {
+ if (ch != '\\' && ch != '<' /* && ch != '@' */ && ch != '$'
+ && ch != '&' && ch != '\r')
+ {
+ ch = *++p;
+ continue;
+ }
+ /* Output any pending text. */
+ if (p - starttext)
+ cnode = addtext(cnode, starttext, p - starttext);
+ /* Ignore \r in DOS line returns */
+ if (ch == '\r') {
+ ch = *++p;
+ starttext = p;
+ continue;
+ }
+ if (ch == '$')
+ locerrorexit(comment->filename, linenum, "use \\$ instead of $");
+ /* See if it is an html named entity. */
+ if (ch == '&' && p[1] != '#') {
+ const char *entity = ENTITIES;
+ /* This search could be faster if the entity names were put
+ * in a hash table or something. */
+ const char *semicolon = strchr(p, ';');
+ unsigned int len;
+ if (!semicolon)
+ locerrorexit(comment->filename, linenum, "unterminated HTML entity");
+ p++;
+ for (;;) {
+ len = strlen(entity);
+ if (!len)
+ locerrorexit(comment->filename, linenum, "unrecognised HTML entity &%.*s;", semicolon - p, p);
+ if (len == semicolon - p && !memcmp(p, entity, len))
+ break;
+ entity += len + 1;
+ entity += strlen(entity) + 1;
+ }
+ entity += len + 1;
+ cnode = addtext(cnode, entity, strlen(entity));
+ p = semicolon + 1;
+ ch = *p;
+ starttext = p;
+ continue;
+ }
+ /* See if it is a backslash escape sequence. */
+ else if (ch == '\\') {
+ const char *match = "\\@&$#<>%";
+ const char *pos;
+ ch = p[1];
+ pos = strchr(match, ch);
+ if (pos) {
+ /* Got a \ escape sequence. */
+ const char *text =
+ "\\\0 @\0 &amp;\0$\0 #\0 &lt;\0 >\0 %"
+ + 6 * (pos - match);
+ cnode = addtext(cnode, text, strlen(text));
+ p += 2;
+ ch = *p;
+ starttext = p;
+ continue;
+ }
+ } else if (ch == '<') {
+ if (incode) {
+ ch = *++p;
+ starttext = p;
+ continue;
+ }
+ /* It's an html tag. */
+ p = parsehtmltag(p, &cnode, comment->filename, &linenum);
+ ch = *p;
+ starttext = p;
+ continue;
+ }
+ {
+ /* Got a doxygen command. First work out its length. */
+ const char *start = ++p;
+ unsigned int cmdlen;
+ const struct command *command;
+ ch = *p;
+ while ((unsigned)((ch & ~0x20) - 'A') <= 'Z' - 'A'
+ || (unsigned)(ch - '0') < 10 || ch == '_' || ch == '-')
+ {
+ ch = *++p;
+ }
+ cmdlen = p - start;
+ if (!cmdlen)
+ locerrorexit(comment->filename, linenum, "\\ or @ without Doxygen command");
+ /* Look it up in the table. */
+ command = commands;
+ for (;;) {
+ if (!command->namelen) {
+ locerrorexit(comment->filename, linenum, "unrecognized Doxygen command '%.*s'",
+ cmdlen + 1, start - 1);
+ }
+ if (command->namelen == cmdlen
+ && !memcmp(command->name, start, cmdlen))
+ {
+ break;
+ }
+ command++;
+ }
+ p = (*command->func)(p, &cnode, command->type,
+ comment->filename, linenum, command->name);
+ ch = *p;
+ starttext = p;
+ }
+ }
+ if (p - starttext) {
+ /* Start new para if there isn't already one going. */
+ if (cnode->funcs->needpara)
+ cnode = startpara(cnode, &para_funcs);
+ cnode = addtext(cnode, starttext, p - starttext);
+ }
+ if (!ch)
+ break;
+ if (cnode->funcs == &text_funcs)
+ addtext(cnode, "\n", 1);
+ p++;
+ }
+ /* Finish the root cnode. */
+ do
+ cnode = endcnode(cnode);
+ while (cnode);
+ assert(!incode);
+ assert(!inhtmlblock);
+}
+
+/***********************************************************************
+ * parsecomments : parse comments
+ *
+ * Enter: comment = first comment in list
+ */
+static void
+parsecomments(struct comment *comment)
+{
+ while (comment) {
+ parsecomment(comment);
+ comment = comment->next;
+ }
+}
+
+/***********************************************************************
+ * attachcommenttonode : attach comment struct to node
+ *
+ * Enter: node = parse node for identifier
+ * comment = comment struct
+ */
+static void
+attachcommenttonode(struct node *node, struct comment *comment)
+{
+ comment->next = node->comments;
+ node->comments = comment;
+}
+
+/***********************************************************************
+ * attachcomments : attach comments to applicable parse nodes
+ *
+ * Enter: comment = first in (reversed) list of comment structs
+ * root = root parse node (for attaching \file comment blocks to)
+ */
+static void
+attachcomments(struct comment *comment, struct node *root)
+{
+ while (comment) {
+ struct comment *next = comment->next;
+ /* See if there are any \param, \return, \throw, \def-api-feature or
+ * \def-device-cap cnodes to detach and attach
+ * elsewhere. (This only looks at top-level nodes, direct children
+ * of the root, so does not detach a \param inside a
+ * \def-device-cap.) */
+ struct cnode **pcnode = &comment->root.children;
+ for (;;) {
+ struct cnode *cnode = *pcnode;
+ if (!cnode)
+ break;
+ if (cnode->funcs == &param_funcs || cnode->funcs == &return_funcs
+ || cnode->funcs == &throw_funcs)
+ {
+ /* Found a \param or \return or \throw to detach. Find the
+ * parameter/exception of the same name, or the return type. */
+ struct node *node;
+ struct comment *newcomment;
+ if (cnode->funcs == &param_funcs) {
+ node = findparamidentifier(comment->node,
+ ((struct paramcnode *)cnode)->name);
+ if (!node)
+ locerrorexit(comment->filename, cnode->linenum, "no parameter '%s' found", ((struct paramcnode *)cnode)->name);
+ } else if (cnode->funcs == &return_funcs) {
+ node = findreturntype(comment->node);
+ if (!node)
+ locerrorexit(comment->filename, cnode->linenum, "no return type found");
+ } else {
+ node = findthrowidentifier(comment->node,
+ ((struct paramcnode *)cnode)->name);
+ if (!node)
+ locerrorexit(comment->filename, cnode->linenum, "no exception '%s' found", ((struct paramcnode *)cnode)->name);
+ }
+ /* Detach the cnode from its old comment. */
+ *pcnode = cnode->next;
+ /* Create a new comment struct to contain this cnode. */
+ newcomment = memalloc(sizeof(struct comment));
+ newcomment->root.funcs = &root_funcs;
+ newcomment->linenum = cnode->linenum;
+ /* Attach the cnode. */
+ newcomment->root.children = cnode;
+ cnode->parent = &newcomment->root;
+ cnode->next = 0;
+ /* Make the cnode a \return one, just so even a \param
+ * uses return_output. */
+ cnode->funcs = &return_funcs;
+ /* Attach the new comment struct to the parse node. */
+ attachcommenttonode(node, newcomment);
+ } else {
+ pcnode = &cnode->next;
+ }
+ }
+ /* Now attach the comment to its identifier parse node. */
+ {
+ struct node *node = comment->node;
+ if (!node)
+ node = root;
+ attachcommenttonode(node, comment);
+ }
+ comment = next;
+ }
+}
+
+/***********************************************************************
+ * processcomments : join, parse and attach comments
+ *
+ * Enter: root = root parse node
+ */
+void
+processcomments(struct node *root)
+{
+ comments = joininlinecomments(comments);
+ parsecomments(comments);
+ attachcomments(comments, root);
+}
+
+/***********************************************************************
+ * outputdescriptive : output descriptive elements for a node
+ *
+ * Enter: node = identifier node that might have some comments
+ * indent = indent (nesting) level
+ */
+void
+outputdescriptive(struct node *node, unsigned int indent)
+{
+ struct comment *comment = node->comments;
+ int indescriptive = 0;
+ while (comment) {
+ struct cnode *root = &comment->root;
+ if (!indescriptive)
+ printf("%*s<descriptive>\n", indent, "");
+ indescriptive = 1;
+ (*root->funcs->output)(root, indent + 2);
+ comment = comment->next;
+ }
+ if (indescriptive)
+ printf("%*s</descriptive>\n", indent, "");
+}