cmark

My personal build of CMark ✏️

Commit
46ac1e61878a6eefea1f3bfff8d25edf8eca0c05
Parent
9219938929274df6398dfe6b9356fd8bbb3d565b
Author
John MacFarlane <jgm@berkeley.edu>
Date

Added width parameter to render_man.

Rewrote man.c using new renderer framework.

Diffstat

4 files changed, 311 insertions, 339 deletions

Status File Name N° Changes Insertions Deletions
Modified man/man3/cmark.3 362 181 181
Modified src/cmark.h 2 1 1
Modified src/main.c 2 1 1
Modified src/man.c 284 128 156
diff --git a/man/man3/cmark.3 b/man/man3/cmark.3
@@ -1,95 +1,95 @@
-.TH cmark 3 "July 03, 2015" "LOCAL" "Library Functions Manual"
-.SH
+.TH cmark 3 "July 12, 2015" "LOCAL" "Library Functions Manual"
+\&.SH
 NAME
-.PP
-\f[B]cmark\f[] \- CommonMark parsing, manipulating, and rendering
+\&.PP
+\ef[B]cmark\ef[] \- CommonMark parsing, manipulating, and rendering
 
-.SH
+\&.SH
 DESCRIPTION
-.SS
+\&.SS
 Simple Interface
 
 .PP
 \fIchar *\f[] \fBcmark_markdown_to_html\f[](\fIconst char *text\f[], \fIsize_t len\f[], \fIint options\f[])
 
-.PP
-Convert \f[I]text\f[] (assumed to be a UTF\-8 encoded string with length
-\f[I]len\f[] from CommonMark Markdown to HTML, returning a null\-terminated,
+\&.PP
+Convert \ef[I]text\ef[] (assumed to be a UTF\-8 encoded string with length
+\ef[I]len\ef[] from CommonMark Markdown to HTML, returning a null\-terminated,
 UTF\-8\-encoded string.
 
-.SS
+\&.SS
 Node Structure
 
-.SS
+\&.SS
 Creating and Destroying Nodes
 
 .PP
 \fIcmark_node*\f[] \fBcmark_node_new\f[](\fIcmark_node_type type\f[])
 
-.PP
-Creates a new node of type \f[I]type\f[]\&.  Note that the node may have
+\&.PP
+Creates a new node of type \ef[I]type\ef[]. Note that the node may have
 other required properties, which it is the caller's responsibility
 to assign.
 
 .PP
 \fIvoid\f[] \fBcmark_node_free\f[](\fIcmark_node *node\f[])
 
-.PP
+\&.PP
 Frees the memory allocated for a node.
 
-.SS
+\&.SS
 Tree Traversal
 
 .PP
 \fIcmark_node*\f[] \fBcmark_node_next\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the next node in the sequence after \f[I]node\f[], or NULL if
+\&.PP
+Returns the next node in the sequence after \ef[I]node\ef[], or NULL if
 there is none.
 
 .PP
 \fIcmark_node*\f[] \fBcmark_node_previous\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the previous node in the sequence after \f[I]node\f[], or NULL if
+\&.PP
+Returns the previous node in the sequence after \ef[I]node\ef[], or NULL if
 there is none.
 
 .PP
 \fIcmark_node*\f[] \fBcmark_node_parent\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the parent of \f[I]node\f[], or NULL if there is none.
+\&.PP
+Returns the parent of \ef[I]node\ef[], or NULL if there is none.
 
 .PP
 \fIcmark_node*\f[] \fBcmark_node_first_child\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the first child of \f[I]node\f[], or NULL if \f[I]node\f[] has no children.
+\&.PP
+Returns the first child of \ef[I]node\ef[], or NULL if \ef[I]node\ef[] has no children.
 
 .PP
 \fIcmark_node*\f[] \fBcmark_node_last_child\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the last child of \f[I]node\f[], or NULL if \f[I]node\f[] has no children.
+\&.PP
+Returns the last child of \ef[I]node\ef[], or NULL if \ef[I]node\ef[] has no children.
 
-.SS
+\&.SS
 Iterator
-.PP
+\&.PP
 An iterator will walk through a tree of nodes, starting from a root
 node, returning one node at a time, together with information about
-whether the node is being entered or exited.  The iterator will
-first descend to a child node, if there is one.  When there is no
-child, the iterator will go to the next sibling.  When there is no
+whether the node is being entered or exited. The iterator will
+first descend to a child node, if there is one. When there is no
+child, the iterator will go to the next sibling. When there is no
 next sibling, the iterator will return to the parent (but with
-a \f[I]cmark_event_type\f[] of \f[C]CMARK_EVENT_EXIT\f[]).  The iterator will
-return \f[C]CMARK_EVENT_DONE\f[] when it reaches the root node again.
-One natural application is an HTML renderer, where an \f[C]ENTER\f[] event
-outputs an open tag and an \f[C]EXIT\f[] event outputs a close tag.
+a \ef[I]cmark_event_type\ef[] of \ef[C]CMARK_EVENT_EXIT\ef[]). The iterator will
+return \ef[C]CMARK_EVENT_DONE\ef[] when it reaches the root node again.
+One natural application is an HTML renderer, where an \ef[C]ENTER\ef[] event
+outputs an open tag and an \ef[C]EXIT\ef[] event outputs a close tag.
 An iterator might also be used to transform an AST in some systematic
 way, for example, turning all level\-3 headers into regular paragraphs.
-.IP
-.nf
-\f[C]
+\&.IP
+\&.nf
+\ef[C]
 void
 usage_example(cmark_node *root) {
     cmark_event_type ev_type;
@@ -102,304 +102,304 @@ usage_example(cmark_node *root) {
 
     cmark_iter_free(iter);
 }
-\f[]
-.fi
-.PP
-Iterators will never return \f[C]EXIT\f[] events for leaf nodes, which are nodes
+\ef[]
+\&.fi
+\&.PP
+Iterators will never return \ef[C]EXIT\ef[] events for leaf nodes, which are nodes
 of type:
-.IP \[bu] 2
+\&.IP \e[bu] 2
 CMARK_NODE_HTML
-.IP \[bu] 2
+\&.IP \e[bu] 2
 CMARK_NODE_HRULE
-.IP \[bu] 2
+\&.IP \e[bu] 2
 CMARK_NODE_CODE_BLOCK
-.IP \[bu] 2
+\&.IP \e[bu] 2
 CMARK_NODE_TEXT
-.IP \[bu] 2
+\&.IP \e[bu] 2
 CMARK_NODE_SOFTBREAK
-.IP \[bu] 2
+\&.IP \e[bu] 2
 CMARK_NODE_LINEBREAK
-.IP \[bu] 2
+\&.IP \e[bu] 2
 CMARK_NODE_CODE
-.IP \[bu] 2
+\&.IP \e[bu] 2
 CMARK_NODE_INLINE_HTML
-.PP
-Nodes must only be modified after an \f[C]EXIT\f[] event, or an \f[C]ENTER\f[] event for
+\&.PP
+Nodes must only be modified after an \ef[C]EXIT\ef[] event, or an \ef[C]ENTER\ef[] event for
 leaf nodes.
 
 .PP
 \fIcmark_iter*\f[] \fBcmark_iter_new\f[](\fIcmark_node *root\f[])
 
-.PP
-Creates a new iterator starting at \f[I]root\f[]\&.  The current node and event
-type are undefined until \f[C]cmark_iter_next\f[] is called for the first time.
+\&.PP
+Creates a new iterator starting at \ef[I]root\ef[]. The current node and event
+type are undefined until \ef[C]cmark_iter_next\ef[] is called for the first time.
 
 .PP
 \fIvoid\f[] \fBcmark_iter_free\f[](\fIcmark_iter *iter\f[])
 
-.PP
+\&.PP
 Frees the memory allocated for an iterator.
 
 .PP
 \fIcmark_event_type\f[] \fBcmark_iter_next\f[](\fIcmark_iter *iter\f[])
 
-.PP
-Advances to the next node and returns the event type (\f[C]CMARK_EVENT_ENTER\f[],
-\f[C]CMARK_EVENT_EXIT\f[] or \f[C]CMARK_EVENT_DONE\f[]).
+\&.PP
+Advances to the next node and returns the event type (\ef[C]CMARK_EVENT_ENTER\ef[],
+\ef[C]CMARK_EVENT_EXIT\ef[] or \ef[C]CMARK_EVENT_DONE\ef[]).
 
 .PP
 \fIcmark_node*\f[] \fBcmark_iter_get_node\f[](\fIcmark_iter *iter\f[])
 
-.PP
+\&.PP
 Returns the current node.
 
 .PP
 \fIcmark_event_type\f[] \fBcmark_iter_get_event_type\f[](\fIcmark_iter *iter\f[])
 
-.PP
+\&.PP
 Returns the current event type.
 
 .PP
 \fIcmark_node*\f[] \fBcmark_iter_get_root\f[](\fIcmark_iter *iter\f[])
 
-.PP
+\&.PP
 Returns the root node.
 
 .PP
 \fIvoid\f[] \fBcmark_iter_reset\f[](\fIcmark_iter *iter\f[], \fIcmark_node *current\f[], \fIcmark_event_type event_type\f[])
 
-.PP
-Resets the iterator so that the current node is \f[I]current\f[] and
-the event type is \f[I]event_type\f[]\&.  The new current node must be a
+\&.PP
+Resets the iterator so that the current node is \ef[I]current\ef[] and
+the event type is \ef[I]event_type\ef[]. The new current node must be a
 descendant of the root node or the root node itself.
 
-.SS
+\&.SS
 Accessors
 
 .PP
 \fIvoid*\f[] \fBcmark_node_get_user_data\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the user data of \f[I]node\f[]\&.
+\&.PP
+Returns the user data of \ef[I]node\ef[].
 
 .PP
 \fIint\f[] \fBcmark_node_set_user_data\f[](\fIcmark_node *node\f[], \fIvoid *user_data\f[])
 
-.PP
-Sets arbitrary user data for \f[I]node\f[]\&.  Returns 1 on success,
+\&.PP
+Sets arbitrary user data for \ef[I]node\ef[]. Returns 1 on success,
 0 on failure.
 
 .PP
 \fIcmark_node_type\f[] \fBcmark_node_get_type\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the type of \f[I]node\f[], or \f[C]CMARK_NODE_NONE\f[] on error.
+\&.PP
+Returns the type of \ef[I]node\ef[], or \ef[C]CMARK_NODE_NONE\ef[] on error.
 
 .PP
 \fIconst char*\f[] \fBcmark_node_get_type_string\f[](\fIcmark_node *node\f[])
 
-.PP
-Like \f[I]cmark_node_get_type\f[], but returns a string representation
-of the type, or \f[C]"<unknown>"\f[]\&.
+\&.PP
+Like \ef[I]cmark_node_get_type\ef[], but returns a string representation
+of the type, or \ef[C]"<unknown>"\ef[].
 
 .PP
 \fIconst char*\f[] \fBcmark_node_get_literal\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the string contents of \f[I]node\f[], or NULL if none.
+\&.PP
+Returns the string contents of \ef[I]node\ef[], or NULL if none.
 
 .PP
 \fIint\f[] \fBcmark_node_set_literal\f[](\fIcmark_node *node\f[], \fIconst char *content\f[])
 
-.PP
-Sets the string contents of \f[I]node\f[]\&.  Returns 1 on success,
+\&.PP
+Sets the string contents of \ef[I]node\ef[]. Returns 1 on success,
 0 on failure.
 
 .PP
 \fIint\f[] \fBcmark_node_get_header_level\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the header level of \f[I]node\f[], or 0 if \f[I]node\f[] is not a header.
+\&.PP
+Returns the header level of \ef[I]node\ef[], or 0 if \ef[I]node\ef[] is not a header.
 
 .PP
 \fIint\f[] \fBcmark_node_set_header_level\f[](\fIcmark_node *node\f[], \fIint level\f[])
 
-.PP
-Sets the header level of \f[I]node\f[], returning 1 on success and 0 on error.
+\&.PP
+Sets the header level of \ef[I]node\ef[], returning 1 on success and 0 on error.
 
 .PP
 \fIcmark_list_type\f[] \fBcmark_node_get_list_type\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the list type of \f[I]node\f[], or \f[C]CMARK_NO_LIST\f[] if \f[I]node\f[]
+\&.PP
+Returns the list type of \ef[I]node\ef[], or \ef[C]CMARK_NO_LIST\ef[] if \ef[I]node\ef[]
 is not a list.
 
 .PP
 \fIint\f[] \fBcmark_node_set_list_type\f[](\fIcmark_node *node\f[], \fIcmark_list_type type\f[])
 
-.PP
-Sets the list type of \f[I]node\f[], returning 1 on success and 0 on error.
+\&.PP
+Sets the list type of \ef[I]node\ef[], returning 1 on success and 0 on error.
 
 .PP
 \fIcmark_delim_type\f[] \fBcmark_node_get_list_delim\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the list delimiter type of \f[I]node\f[], or \f[C]CMARK_NO_DELIM\f[] if \f[I]node\f[]
+\&.PP
+Returns the list delimiter type of \ef[I]node\ef[], or \ef[C]CMARK_NO_DELIM\ef[] if \ef[I]node\ef[]
 is not a list.
 
 .PP
 \fIint\f[] \fBcmark_node_set_list_delim\f[](\fIcmark_node *node\f[], \fIcmark_delim_type delim\f[])
 
-.PP
-Sets the list delimiter type of \f[I]node\f[], returning 1 on success and 0
+\&.PP
+Sets the list delimiter type of \ef[I]node\ef[], returning 1 on success and 0
 on error.
 
 .PP
 \fIint\f[] \fBcmark_node_get_list_start\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns starting number of \f[I]node\f[], if it is an ordered list, otherwise 0.
+\&.PP
+Returns starting number of \ef[I]node\ef[], if it is an ordered list, otherwise 0.
 
 .PP
 \fIint\f[] \fBcmark_node_set_list_start\f[](\fIcmark_node *node\f[], \fIint start\f[])
 
-.PP
-Sets starting number of \f[I]node\f[], if it is an ordered list. Returns 1
+\&.PP
+Sets starting number of \ef[I]node\ef[], if it is an ordered list. Returns 1
 on success, 0 on failure.
 
 .PP
 \fIint\f[] \fBcmark_node_get_list_tight\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns 1 if \f[I]node\f[] is a tight list, 0 otherwise.
+\&.PP
+Returns 1 if \ef[I]node\ef[] is a tight list, 0 otherwise.
 
 .PP
 \fIint\f[] \fBcmark_node_set_list_tight\f[](\fIcmark_node *node\f[], \fIint tight\f[])
 
-.PP
-Sets the "tightness" of a list.  Returns 1 on success, 0 on failure.
+\&.PP
+Sets the "tightness" of a list. Returns 1 on success, 0 on failure.
 
 .PP
 \fIconst char*\f[] \fBcmark_node_get_fence_info\f[](\fIcmark_node *node\f[])
 
-.PP
+\&.PP
 Returns the info string from a fenced code block, or NULL if none.
 
 .PP
 \fIint\f[] \fBcmark_node_set_fence_info\f[](\fIcmark_node *node\f[], \fIconst char *info\f[])
 
-.PP
+\&.PP
 Sets the info string in a fenced code block, returning 1 on
 success and 0 on failure.
 
 .PP
 \fIconst char*\f[] \fBcmark_node_get_url\f[](\fIcmark_node *node\f[])
 
-.PP
-Gets the URL of a link or image \f[I]node\f[], or NULL if none.
+\&.PP
+Gets the URL of a link or image \ef[I]node\ef[], or NULL if none.
 
 .PP
 \fIint\f[] \fBcmark_node_set_url\f[](\fIcmark_node *node\f[], \fIconst char *url\f[])
 
-.PP
-Sets the URL of a link or image \f[I]node\f[]\&. Returns 1 on success,
+\&.PP
+Sets the URL of a link or image \ef[I]node\ef[]. Returns 1 on success,
 0 on failure.
 
 .PP
 \fIconst char*\f[] \fBcmark_node_get_title\f[](\fIcmark_node *node\f[])
 
-.PP
-Gets the title of a link or image \f[I]node\f[], or NULL if none.
+\&.PP
+Gets the title of a link or image \ef[I]node\ef[], or NULL if none.
 
 .PP
 \fIint\f[] \fBcmark_node_set_title\f[](\fIcmark_node *node\f[], \fIconst char *title\f[])
 
-.PP
-Sets the title of a link or image \f[I]node\f[]\&. Returns 1 on success,
+\&.PP
+Sets the title of a link or image \ef[I]node\ef[]. Returns 1 on success,
 0 on failure.
 
 .PP
 \fIint\f[] \fBcmark_node_get_start_line\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the line on which \f[I]node\f[] begins.
+\&.PP
+Returns the line on which \ef[I]node\ef[] begins.
 
 .PP
 \fIint\f[] \fBcmark_node_get_start_column\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the column at which \f[I]node\f[] begins.
+\&.PP
+Returns the column at which \ef[I]node\ef[] begins.
 
 .PP
 \fIint\f[] \fBcmark_node_get_end_line\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the line on which \f[I]node\f[] ends.
+\&.PP
+Returns the line on which \ef[I]node\ef[] ends.
 
 .PP
 \fIint\f[] \fBcmark_node_get_end_column\f[](\fIcmark_node *node\f[])
 
-.PP
-Returns the column at which \f[I]node\f[] ends.
+\&.PP
+Returns the column at which \ef[I]node\ef[] ends.
 
-.SS
+\&.SS
 Tree Manipulation
 
 .PP
 \fIvoid\f[] \fBcmark_node_unlink\f[](\fIcmark_node *node\f[])
 
-.PP
-Unlinks a \f[I]node\f[], removing it from the tree, but not freeing its
-memory.  (Use \f[I]cmark_node_free\f[] for that.)
+\&.PP
+Unlinks a \ef[I]node\ef[], removing it from the tree, but not freeing its
+memory. (Use \ef[I]cmark_node_free\ef[] for that.)
 
 .PP
 \fIint\f[] \fBcmark_node_insert_before\f[](\fIcmark_node *node\f[], \fIcmark_node *sibling\f[])
 
-.PP
-Inserts \f[I]sibling\f[] before \f[I]node\f[]\&.  Returns 1 on success, 0 on failure.
+\&.PP
+Inserts \ef[I]sibling\ef[] before \ef[I]node\ef[]. Returns 1 on success, 0 on failure.
 
 .PP
 \fIint\f[] \fBcmark_node_insert_after\f[](\fIcmark_node *node\f[], \fIcmark_node *sibling\f[])
 
-.PP
-Inserts \f[I]sibling\f[] after \f[I]node\f[]\&. Returns 1 on success, 0 on failure.
+\&.PP
+Inserts \ef[I]sibling\ef[] after \ef[I]node\ef[]. Returns 1 on success, 0 on failure.
 
 .PP
 \fIint\f[] \fBcmark_node_prepend_child\f[](\fIcmark_node *node\f[], \fIcmark_node *child\f[])
 
-.PP
-Adds \f[I]child\f[] to the beginning of the children of \f[I]node\f[]\&.
+\&.PP
+Adds \ef[I]child\ef[] to the beginning of the children of \ef[I]node\ef[].
 Returns 1 on success, 0 on failure.
 
 .PP
 \fIint\f[] \fBcmark_node_append_child\f[](\fIcmark_node *node\f[], \fIcmark_node *child\f[])
 
-.PP
-Adds \f[I]child\f[] to the end of the children of \f[I]node\f[]\&.
+\&.PP
+Adds \ef[I]child\ef[] to the end of the children of \ef[I]node\ef[].
 Returns 1 on success, 0 on failure.
 
 .PP
 \fIvoid\f[] \fBcmark_consolidate_text_nodes\f[](\fIcmark_node *root\f[])
 
-.PP
+\&.PP
 Consolidates adjacent text nodes.
 
-.SS
+\&.SS
 Parsing
-.PP
+\&.PP
 Simple interface:
-.IP
-.nf
-\f[C]
+\&.IP
+\&.nf
+\ef[C]
 cmark_node *document = cmark_parse_document("Hello *world*", 12,
                                             CMARK_OPT_DEFAULT);
-\f[]
-.fi
-.PP
+\ef[]
+\&.fi
+\&.PP
 Streaming interface:
-.IP
-.nf
-\f[C]
+\&.IP
+\&.nf
+\ef[C]
 cmark_parser *parser = cmark_parser_new(CMARK_OPT_DEFAULT);
 FILE *fp = fopen("myfile.md", "r");
 while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
@@ -410,80 +410,80 @@ while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
 }
 document = cmark_parser_finish(parser);
 cmark_parser_free(parser);
-\f[]
-.fi
+\ef[]
+\&.fi
 
 .PP
 \fIcmark_parser *\f[] \fBcmark_parser_new\f[](\fIint options\f[])
 
-.PP
+\&.PP
 Creates a new parser object.
 
 .PP
 \fIvoid\f[] \fBcmark_parser_free\f[](\fIcmark_parser *parser\f[])
 
-.PP
+\&.PP
 Frees memory allocated for a parser object.
 
 .PP
 \fIvoid\f[] \fBcmark_parser_feed\f[](\fIcmark_parser *parser\f[], \fIconst char *buffer\f[], \fIsize_t len\f[])
 
-.PP
-Feeds a string of length \f[I]len\f[] to \f[I]parser\f[]\&.
+\&.PP
+Feeds a string of length \ef[I]len\ef[] to \ef[I]parser\ef[].
 
 .PP
 \fIcmark_node *\f[] \fBcmark_parser_finish\f[](\fIcmark_parser *parser\f[])
 
-.PP
+\&.PP
 Finish parsing and return a pointer to a tree of nodes.
 
 .PP
 \fIcmark_node *\f[] \fBcmark_parse_document\f[](\fIconst char *buffer\f[], \fIsize_t len\f[], \fIint options\f[])
 
-.PP
-Parse a CommonMark document in \f[I]buffer\f[] of length \f[I]len\f[]\&.
+\&.PP
+Parse a CommonMark document in \ef[I]buffer\ef[] of length \ef[I]len\ef[].
 Returns a pointer to a tree of nodes.
 
 .PP
 \fIcmark_node *\f[] \fBcmark_parse_file\f[](\fIFILE *f\f[], \fIint options\f[])
 
-.PP
-Parse a CommonMark document in file \f[I]f\f[], returning a pointer to
+\&.PP
+Parse a CommonMark document in file \ef[I]f\ef[], returning a pointer to
 a tree of nodes.
 
-.SS
+\&.SS
 Rendering
 
 .PP
 \fIchar *\f[] \fBcmark_render_xml\f[](\fIcmark_node *root\f[], \fIint options\f[])
 
-.PP
-Render a \f[I]node\f[] tree as XML.
+\&.PP
+Render a \ef[I]node\ef[] tree as XML.
 
 .PP
 \fIchar *\f[] \fBcmark_render_html\f[](\fIcmark_node *root\f[], \fIint options\f[])
 
-.PP
-Render a \f[I]node\f[] tree as an HTML fragment.  It is up to the user
+\&.PP
+Render a \ef[I]node\ef[] tree as an HTML fragment. It is up to the user
 to add an appropriate header and footer.
 
 .PP
-\fIchar *\f[] \fBcmark_render_man\f[](\fIcmark_node *root\f[], \fIint options\f[])
+\fIchar *\f[] \fBcmark_render_man\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[])
 
-.PP
-Render a \f[I]node\f[] tree as a groff man page, without the header.
+\&.PP
+Render a \ef[I]node\ef[] tree as a groff man page, without the header.
 
 .PP
 \fIchar *\f[] \fBcmark_render_commonmark\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[])
 
-.PP
-Render a \f[I]node\f[] tree as a commonmark document.
+\&.PP
+Render a \ef[I]node\ef[] tree as a commonmark document.
 
 .PP
 \fIchar *\f[] \fBcmark_render_latex\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[])
 
-.PP
-Render a \f[I]node\f[] tree as a LaTeX document.
+\&.PP
+Render a \ef[I]node\ef[] tree as a LaTeX document.
 
 .PP
 .nf
@@ -494,7 +494,7 @@ Render a \f[I]node\f[] tree as a LaTeX document.
 \f[]
 .fi
 
-.PP
+\&.PP
 Default writer options.
 
 .PP
@@ -506,8 +506,8 @@ Default writer options.
 \f[]
 .fi
 
-.PP
-Include a \f[C]data\-sourcepos\f[] attribute on all block elements.
+\&.PP
+Include a \ef[C]data\-sourcepos\ef[] attribute on all block elements.
 
 .PP
 .nf
@@ -518,8 +518,8 @@ Include a \f[C]data\-sourcepos\f[] attribute on all block elements.
 \f[]
 .fi
 
-.PP
-Render \f[C]softbreak\f[] elements as hard line breaks.
+\&.PP
+Render \ef[C]softbreak\ef[] elements as hard line breaks.
 
 .PP
 .nf
@@ -530,7 +530,7 @@ Render \f[C]softbreak\f[] elements as hard line breaks.
 \f[]
 .fi
 
-.PP
+\&.PP
 Normalize tree by consolidating adjacent text nodes.
 
 .PP
@@ -542,7 +542,7 @@ Normalize tree by consolidating adjacent text nodes.
 \f[]
 .fi
 
-.PP
+\&.PP
 Convert straight quotes to curly, \-\-\- to em dashes, \-\- to en dashes.
 
 .PP
@@ -554,37 +554,37 @@ Convert straight quotes to curly, \-\-\- to em dashes, \-\- to en dashes.
 \f[]
 .fi
 
-.PP
+\&.PP
 Validate UTF\-8 in the input before parsing, replacing illegal
 sequences with the replacement character U+FFFD.
 
-.SS
+\&.SS
 Version information
 
 .PP
 \fIint\f[] \fBcmark_version\f[](\fI\f[])
 
-.PP
+\&.PP
 The library version as integer for runtime checks. Also available as
 macro CMARK_VERSION for compile time checks.
-.IP \[bu] 2
+\&.IP \e[bu] 2
 Bits 16\-23 contain the major version.
-.IP \[bu] 2
+\&.IP \e[bu] 2
 Bits 8\-15 contain the minor version.
-.IP \[bu] 2
+\&.IP \e[bu] 2
 Bits 0\-7 contain the patchlevel.
-.PP
+\&.PP
 In hexadecimal format, the number 0x010203 represents version 1.2.3.
 
 .PP
 \fIconst char *\f[] \fBcmark_version_string\f[](\fI\f[])
 
-.PP
+\&.PP
 The library version string for runtime checks. Also available as
 macro CMARK_VERSION_STRING for compile time checks.
 
-.SH
+\&.SH
 AUTHORS
-.PP
-John MacFarlane, Vicent Marti,  Kārlis Gaņģis, Nick Wellnhofer.
+\&.PP
+John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
 
diff --git a/src/cmark.h b/src/cmark.h
@@ -479,7 +479,7 @@ char *cmark_render_html(cmark_node *root, int options);
 /** Render a 'node' tree as a groff man page, without the header.
  */
 CMARK_EXPORT
-char *cmark_render_man(cmark_node *root, int options);
+char *cmark_render_man(cmark_node *root, int options, int width);
 
 /** Render a 'node' tree as a commonmark document.
  */
diff --git a/src/main.c b/src/main.c
@@ -47,7 +47,7 @@ static void print_document(cmark_node *document, writer_format writer,
 		result = cmark_render_xml(document, options);
 		break;
 	case FORMAT_MAN:
-		result = cmark_render_man(document, options);
+		result = cmark_render_man(document, options, width);
 		break;
 	case FORMAT_COMMONMARK:
 		result = cmark_render_commonmark(document, options, width);
diff --git a/src/man.c b/src/man.c
@@ -8,110 +8,93 @@
 #include "node.h"
 #include "buffer.h"
 #include "utf8.h"
+#include "render.h"
 
-// Functions to convert cmark_nodes to groff man strings.
+#define OUT(s, wrap, escaping) renderer->out(renderer, s, wrap, escaping)
+#define LIT(s) renderer->out(renderer, s, false, LITERAL)
+#define CR() renderer->cr(renderer)
+#define BLANKLINE() renderer->blankline(renderer)
 
-static void escape_man(cmark_strbuf *dest, const unsigned char *source, int length)
+// Functions to convert cmark_nodes to groff man strings.
+static
+void S_outc(cmark_renderer *renderer,
+	    cmark_escaping escape,
+	    int32_t c,
+	    unsigned char nextc)
 {
-	int32_t c;
-	int i = 0;
-	int len;
-	bool beginLine = true;
-
-	while (i < length) {
-		len = utf8proc_iterate(source + i, length - i, &c);
-		if (len == -1) { // error condition
-			return;  // return without rendering anything
+	(void)(escape); // avoid unused parameter warning
+	(void)(nextc);
+
+	switch(c) {
+	case 46:
+		if (renderer->begin_line) {
+			cmark_strbuf_puts(renderer->buffer, "\\&.");
+			renderer->column += 3;
+		} else {
+			utf8proc_encode_char(c, renderer->buffer);
+			renderer->column += 1;
 		}
-		switch(c) {
-		case 46:
-			if (beginLine) {
-				cmark_strbuf_puts(dest, "\\&.");
-			} else {
-				utf8proc_encode_char(c, dest);
-			}
-			break;
-		case 39:
-			if (beginLine) {
-				cmark_strbuf_puts(dest, "\\&'");
-			} else {
-				utf8proc_encode_char(c, dest);
-			}
-			break;
-		case 45:
-			cmark_strbuf_puts(dest, "\\-");
-			break;
-		case 92:
-			cmark_strbuf_puts(dest, "\\e");
-			break;
-		case 8216: // left single quote
-			cmark_strbuf_puts(dest, "\\[oq]");
-			break;
-		case 8217: // right single quote
-			cmark_strbuf_puts(dest, "\\[cq]");
-			break;
-		case 8220: // left double quote
-			cmark_strbuf_puts(dest, "\\[lq]");
-			break;
-		case 8221: // right double quote
-			cmark_strbuf_puts(dest, "\\[rq]");
-			break;
-		case 8212: // em dash
-			cmark_strbuf_puts(dest, "\\[em]");
-			break;
-		case 8211: // en dash
-			cmark_strbuf_puts(dest, "\\[en]");
-			break;
-		default:
-			utf8proc_encode_char(c, dest);
+		break;
+	case 39:
+		if (renderer->begin_line) {
+			cmark_strbuf_puts(renderer->buffer, "\\&'");
+			renderer->column += 3;
+		} else {
+			utf8proc_encode_char(c, renderer->buffer);
+			renderer->column += 1;
 		}
-		beginLine = (c == 10);
-		i += len;
+		break;
+	case 45:
+		cmark_strbuf_puts(renderer->buffer, "\\-");
+		renderer->column += 2;
+		break;
+	case 92:
+		cmark_strbuf_puts(renderer->buffer, "\\e");
+		renderer->column += 2;
+		break;
+	case 8216: // left single quote
+		cmark_strbuf_puts(renderer->buffer, "\\[oq]");
+		renderer->column += 5;
+		break;
+	case 8217: // right single quote
+		cmark_strbuf_puts(renderer->buffer, "\\[cq]");
+		renderer->column += 5;
+		break;
+	case 8220: // left double quote
+		cmark_strbuf_puts(renderer->buffer, "\\[lq]");
+		renderer->column += 5;
+		break;
+	case 8221: // right double quote
+		cmark_strbuf_puts(renderer->buffer, "\\[rq]");
+		renderer->column += 5;
+		break;
+	case 8212: // em dash
+		cmark_strbuf_puts(renderer->buffer, "\\[em]");
+		renderer->column += 5;
+		break;
+	case 8211: // en dash
+		cmark_strbuf_puts(renderer->buffer, "\\[en]");
+		renderer->column += 5;
+		break;
+	default:
+		utf8proc_encode_char(c, renderer->buffer);
+		renderer->column += 1;
 	}
+	renderer->begin_line = (c == 10);
 }
 
-static inline void cr(cmark_strbuf *man)
-{
-	if (man->size && man->ptr[man->size - 1] != '\n')
-		cmark_strbuf_putc(man, '\n');
-}
-
-struct render_state {
-	cmark_strbuf* man;
-	cmark_node *plain;
-};
-
 static int
-S_render_node(cmark_node *node, cmark_event_type ev_type,
-              struct render_state *state)
+S_render_node(cmark_renderer *renderer,
+	      cmark_node *node,
+	      cmark_event_type ev_type,
+              int options)
 {
 	cmark_node *tmp;
-	cmark_strbuf *man = state->man;
 	int list_number;
 	bool entering = (ev_type == CMARK_EVENT_ENTER);
 
-	if (state->plain == node) { // back at original node
-		state->plain = NULL;
-	}
-
-	if (state->plain != NULL) {
-		switch(node->type) {
-		case CMARK_NODE_TEXT:
-		case CMARK_NODE_CODE:
-			escape_man(man, node->as.literal.data,
-			           node->as.literal.len);
-			break;
-
-		case CMARK_NODE_LINEBREAK:
-		case CMARK_NODE_SOFTBREAK:
-			cmark_strbuf_putc(man, ' ');
-			break;
-
-		default:
-			break;
-		}
-		return 1;
-	}
+	// avoid unused parameter error:
+	(void)(options);
 
 	switch (node->type) {
 	case CMARK_NODE_DOCUMENT:
@@ -119,13 +102,13 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
 
 	case CMARK_NODE_BLOCK_QUOTE:
 		if (entering) {
-			cr(man);
-			cmark_strbuf_puts(man, ".RS");
-			cr(man);
+			CR();
+			LIT(".RS");
+			CR();
 		} else {
-			cr(man);
-			cmark_strbuf_puts(man, ".RE");
-			cr(man);
+			CR();
+			LIT(".RE");
+			CR();
 		}
 		break;
 
@@ -134,11 +117,11 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
 
 	case CMARK_NODE_ITEM:
 		if (entering) {
-			cr(man);
-			cmark_strbuf_puts(man, ".IP ");
+			CR();
+			LIT(".IP ");
 			if (cmark_node_get_list_type(node->parent) ==
 			    CMARK_BULLET_LIST) {
-				cmark_strbuf_puts(man, "\\[bu] 2");
+				LIT("\\[bu] 2");
 			} else {
 				list_number = cmark_node_get_list_start(node->parent);
 				tmp = node;
@@ -146,43 +129,45 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
 					tmp = tmp->prev;
 					list_number += 1;
 				}
-				cmark_strbuf_printf(man, "\"%d.\" 4", list_number);
+				char list_number_s[20];
+				sprintf(list_number_s, "\"%d.\" 4", list_number);
+				LIT(list_number_s);
 			}
-			cr(man);
+			CR();
 		} else {
-			cr(man);
+			CR();
 		}
 		break;
 
 	case CMARK_NODE_HEADER:
 		if (entering) {
-			cr(man);
-			cmark_strbuf_puts(man,
-			                  cmark_node_get_header_level(node) == 1 ?
+			CR();
+			LIT(cmark_node_get_header_level(node) == 1 ?
 			                  ".SH" : ".SS");
-			cr(man);
+			CR();
 		} else {
-			cr(man);
+			CR();
 		}
 		break;
 
 	case CMARK_NODE_CODE_BLOCK:
-		cr(man);
-		cmark_strbuf_puts(man, ".IP\n.nf\n\\f[C]\n");
-		escape_man(man, node->as.code.literal.data,
-		           node->as.code.literal.len);
-		cr(man);
-		cmark_strbuf_puts(man, "\\f[]\n.fi");
-		cr(man);
+		CR();
+		LIT(".IP\n.nf\n\\f[C]\n");
+		OUT(cmark_node_get_literal(node),
+		    false,
+		    NORMAL);
+		CR();
+		LIT("\\f[]\n.fi");
+		CR();
 		break;
 
 	case CMARK_NODE_HTML:
 		break;
 
 	case CMARK_NODE_HRULE:
-		cr(man);
-		cmark_strbuf_puts(man, ".PP\n  *  *  *  *  *");
-		cr(man);
+		CR();
+		LIT(".PP\n  *  *  *  *  *");
+		CR();
 		break;
 
 	case CMARK_NODE_PARAGRAPH:
@@ -193,32 +178,36 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
 			    node->prev == NULL) {
 				// no blank line or .PP
 			} else {
-				cr(man);
-				cmark_strbuf_puts(man, ".PP\n");
+				CR();
+				LIT(".PP");
+				CR();
 			}
 		} else {
-			cr(man);
+			CR();
 		}
 		break;
 
 	case CMARK_NODE_TEXT:
-		escape_man(man, node->as.literal.data,
-		           node->as.literal.len);
+		OUT(cmark_node_get_literal(node), true, NORMAL);
 		break;
 
 	case CMARK_NODE_LINEBREAK:
-		cmark_strbuf_puts(man, ".PD 0\n.P\n.PD");
-		cr(man);
+		LIT(".PD 0\n.P\n.PD");
+		CR();
 		break;
 
 	case CMARK_NODE_SOFTBREAK:
-		cmark_strbuf_putc(man, '\n');
+		if (renderer->width == 0) {
+			CR();
+		} else {
+			OUT(" ", true, LITERAL);
+		}
 		break;
 
 	case CMARK_NODE_CODE:
-		cmark_strbuf_puts(man, "\\f[C]");
-		escape_man(man, node->as.literal.data, node->as.literal.len);
-		cmark_strbuf_puts(man, "\\f[]");
+		LIT("\\f[C]");
+		OUT(cmark_node_get_literal(node), true, NORMAL);
+		LIT("\\f[]");
 		break;
 
 	case CMARK_NODE_INLINE_HTML:
@@ -226,33 +215,33 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
 
 	case CMARK_NODE_STRONG:
 		if (entering) {
-			cmark_strbuf_puts(man, "\\f[B]");
+			LIT("\\f[B]");
 		} else {
-			cmark_strbuf_puts(man, "\\f[]");
+			LIT("\\f[]");
 		}
 		break;
 
 	case CMARK_NODE_EMPH:
 		if (entering) {
-			cmark_strbuf_puts(man, "\\f[I]");
+			LIT("\\f[I]");
 		} else {
-			cmark_strbuf_puts(man, "\\f[]");
+			LIT("\\f[]");
 		}
 		break;
 
 	case CMARK_NODE_LINK:
 		if (!entering) {
-			cmark_strbuf_printf(man, " (%s)",
-			                    cmark_node_get_url(node));
+			LIT(" (");
+			OUT(cmark_node_get_url(node), true, URL);
+			LIT(")");
 		}
 		break;
 
 	case CMARK_NODE_IMAGE:
 		if (entering) {
-			cmark_strbuf_puts(man, "[IMAGE: ");
-			state->plain = node;
+			LIT("[IMAGE: ");
 		} else {
-			cmark_strbuf_puts(man, "]");
+			LIT("]");
 		}
 		break;
 
@@ -261,27 +250,10 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
 		break;
 	}
 
-	// cmark_strbuf_putc(man, 'x');
 	return 1;
 }
 
-char *cmark_render_man(cmark_node *root, int options)
+char *cmark_render_man(cmark_node *root, int options, int width)
 {
-	char *result;
-	cmark_strbuf man = GH_BUF_INIT;
-	struct render_state state = { &man, NULL };
-	cmark_node *cur;
-	cmark_event_type ev_type;
-	cmark_iter *iter = cmark_iter_new(root);
-
-	if (options == 0) options = 0; // avoid warning about unused parameters
-
-	while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
-		cur = cmark_iter_get_node(iter);
-		S_render_node(cur, ev_type, &state);
-	}
-	result = (char *)cmark_strbuf_detach(&man);
-
-	cmark_iter_free(iter);
-	return result;
+	return cmark_render(root, options, width, S_outc, S_render_node);
 }