Switch man page creator to Markdown

Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/9c7b59c2
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/9c7b59c2
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/9c7b59c2

Branch: refs/heads/master
Commit: 9c7b59c2c3df1c740c64040772f0ba704621131c
Parents: 21253ae
Author: Nick Wellnhofer <[email protected]>
Authored: Sun Nov 9 16:38:23 2014 +0100
Committer: Nick Wellnhofer <[email protected]>
Committed: Wed Dec 24 16:02:04 2014 +0100

----------------------------------------------------------------------
 compiler/src/CFCCMan.c | 525 ++++++++++++++++++++++++++++++--------------
 1 file changed, 358 insertions(+), 167 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/9c7b59c2/compiler/src/CFCCMan.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCCMan.c b/compiler/src/CFCCMan.c
index 2ddce72..736d0f0 100644
--- a/compiler/src/CFCCMan.c
+++ b/compiler/src/CFCCMan.c
@@ -16,8 +16,12 @@
 
 #include <string.h>
 
+#include <cmark.h>
+
 #include "charmony.h"
 #include "CFCCMan.h"
+#include "CFCBase.h"
+#include "CFCC.h"
 #include "CFCClass.h"
 #include "CFCDocuComment.h"
 #include "CFCFunction.h"
@@ -25,6 +29,7 @@
 #include "CFCParamList.h"
 #include "CFCSymbol.h"
 #include "CFCType.h"
+#include "CFCUri.h"
 #include "CFCUtil.h"
 #include "CFCVariable.h"
 
@@ -33,12 +38,6 @@
     #define false 0
 #endif
 
-typedef struct CFCPodLink {
-    size_t      total_size;
-    const char *text;
-    size_t      text_size;
-} CFCPodLink;
-
 static char*
 S_man_create_name(CFCClass *klass);
 
@@ -55,23 +54,25 @@ static char*
 S_man_create_methods(CFCClass *klass);
 
 static char*
-S_man_create_inherited_methods(CFCClass *klass);
+S_man_create_fresh_methods(CFCClass *klass, CFCClass *ancestor);
 
 static char*
-S_man_create_func(CFCClass *klass, CFCFunction *func, const char *short_sym,
-                  const char *full_sym);
+S_man_create_func(CFCClass *klass, CFCFunction *func, const char *full_sym);
 
 static char*
-S_man_create_param_list(CFCFunction *func, const char *full_sym);
+S_man_create_param_list(CFCClass *klass, CFCFunction *func);
 
 static char*
 S_man_create_inheritance(CFCClass *klass);
 
 static char*
-S_man_escape_content(const char *content);
+S_md_to_man(CFCClass *klass, const char *md, int needs_indent);
 
-static void
-S_parse_pod_link(const char *content, CFCPodLink *pod_link);
+static char*
+S_nodes_to_man(CFCClass *klass, cmark_node *node, int needs_indent);
+
+static char*
+S_man_escape(const char *content);
 
 char*
 CFCCMan_create_man_page(CFCClass *klass) {
@@ -125,17 +126,19 @@ S_man_create_name(CFCClass *klass) {
     char *result = CFCUtil_strdup(".SH NAME\n");
     result = CFCUtil_cat(result, CFCClass_get_class_name(klass), NULL);
 
+    const char *raw_brief = NULL;
     CFCDocuComment *docucom = CFCClass_get_docucomment(klass);
     if (docucom) {
-        const char *raw_brief = CFCDocuComment_get_brief(docucom);
-        if (raw_brief && raw_brief[0] != '\0') {
-            char *brief = S_man_escape_content(raw_brief);
-            result = CFCUtil_cat(result, " \\- ", brief, NULL);
-            FREEMEM(brief);
-        }
+        raw_brief = CFCDocuComment_get_brief(docucom);
+    }
+    if (raw_brief && raw_brief[0] != '\0') {
+        char *brief = S_md_to_man(klass, raw_brief, false);
+        result = CFCUtil_cat(result, " \\- ", brief, NULL);
+        FREEMEM(brief);
+    }
+    else {
+        result = CFCUtil_cat(result, "\n", NULL);
     }
-
-    result = CFCUtil_cat(result, "\n", NULL);
 
     return result;
 }
@@ -156,8 +159,8 @@ S_man_create_description(CFCClass *klass) {
     const char *raw_description = CFCDocuComment_get_long(docucom);
     if (!raw_description || raw_description[0] == '\0') { return result; }
 
-    char *description = S_man_escape_content(raw_description);
-    result = CFCUtil_cat(result, ".SH DESCRIPTION\n", description, "\n", NULL);
+    char *description = S_md_to_man(klass, raw_description, false);
+    result = CFCUtil_cat(result, ".SH DESCRIPTION\n", description, NULL);
     FREEMEM(description);
 
     return result;
@@ -176,13 +179,13 @@ S_man_create_functions(CFCClass *klass) {
             result = CFCUtil_cat(result, ".SH FUNCTIONS\n", NULL);
         }
 
-        const char *micro_sym     = CFCFunction_micro_sym(func);
-        const char *full_func_sym = CFCFunction_full_func_sym(func);
+        const char *micro_sym = CFCFunction_micro_sym(func);
+        result = CFCUtil_cat(result, ".TP\n.B ", micro_sym, "\n", NULL);
 
-        char *redman = S_man_create_func(klass, func, micro_sym,
-                                         full_func_sym);
-        result = CFCUtil_cat(result, redman, NULL);
-        FREEMEM(redman);
+        const char *full_func_sym = CFCFunction_full_func_sym(func);
+        char *function_man = S_man_create_func(klass, func, full_func_sym);
+        result = CFCUtil_cat(result, function_man, NULL);
+        FREEMEM(function_man);
     }
 
     return result;
@@ -190,53 +193,31 @@ S_man_create_functions(CFCClass *klass) {
 
 static char*
 S_man_create_methods(CFCClass *klass) {
-    CFCMethod **fresh_methods = CFCClass_fresh_methods(klass);
-    char       *methods_man   = CFCUtil_strdup("");
-    char       *novel_man     = CFCUtil_strdup("");
-    char       *result;
-
-    for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
-        CFCMethod *method = fresh_methods[meth_num];
-        if (!CFCMethod_public(method) || !CFCMethod_novel(method)) {
-            continue;
+    char *methods_man = CFCUtil_strdup("");
+    char *result;
+
+    for (CFCClass *ancestor = klass;
+         ancestor;
+         ancestor = CFCClass_get_parent(ancestor)
+    ) {
+        const char *class_name = CFCClass_get_class_name(ancestor);
+        // Exclude methods inherited from Clownfish::Obj
+        if (ancestor != klass && strcmp(class_name, "Clownfish::Obj") == 0) {
+            break;
         }
 
-        const char *macro_sym = CFCMethod_get_macro_sym(method);
-        char *full_method_sym = CFCMethod_full_method_sym(method, NULL);
-        char *method_man = S_man_create_func(klass, (CFCFunction*)method,
-                                             macro_sym, full_method_sym);
-
-        if (CFCMethod_abstract(method)) {
-            if (methods_man[0] == '\0') {
-                methods_man = CFCUtil_cat(methods_man,
-                                          ".SS Abstract methods\n", NULL);
+        char *fresh_man = S_man_create_fresh_methods(klass, ancestor);
+        if (fresh_man[0] != '\0') {
+            if (ancestor == klass) {
+                methods_man = CFCUtil_cat(methods_man, fresh_man, NULL);
             }
-            methods_man = CFCUtil_cat(methods_man, method_man, NULL);
-        }
-        else {
-            if (novel_man[0] == '\0') {
-                novel_man = CFCUtil_cat(novel_man,
-                                        ".SS Novel methods\n", NULL);
+            else {
+                methods_man
+                    = CFCUtil_cat(methods_man, ".SS Methods inherited from ",
+                                  class_name, "\n", fresh_man, NULL);
             }
-            novel_man = CFCUtil_cat(novel_man, method_man, NULL);
         }
-
-        FREEMEM(method_man);
-        FREEMEM(full_method_sym);
-    }
-
-    methods_man = CFCUtil_cat(methods_man, novel_man, NULL);
-
-    // Add methods from parent classes excluding Clownfish::Obj
-    CFCClass *parent = CFCClass_get_parent(klass);
-    while (parent) {
-        if (strcmp(CFCClass_get_class_name(parent), "Clownfish::Obj") == 0) {
-            break;
-        }
-        char *inherited_man = S_man_create_inherited_methods(parent);
-        methods_man = CFCUtil_cat(methods_man, inherited_man, NULL);
-        FREEMEM(inherited_man);
-        parent = CFCClass_get_parent(parent);
+        FREEMEM(fresh_man);
     }
 
     if (methods_man[0] == '\0') {
@@ -247,42 +228,48 @@ S_man_create_methods(CFCClass *klass) {
     }
 
     FREEMEM(methods_man);
-    FREEMEM(novel_man);
     return result;
 }
 
 static char*
-S_man_create_inherited_methods(CFCClass *klass) {
-    CFCMethod **fresh_methods = CFCClass_fresh_methods(klass);
-    char       *result        = CFCUtil_strdup("");
+S_man_create_fresh_methods(CFCClass *klass, CFCClass *ancestor) {
+    CFCMethod  **fresh_methods = CFCClass_fresh_methods(klass);
+    const char  *ancestor_name = CFCClass_get_class_name(ancestor);
+    char        *result        = CFCUtil_strdup("");
 
     for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) {
         CFCMethod *method = fresh_methods[meth_num];
-        if (!CFCMethod_public(method) || !CFCMethod_novel(method)) {
+        if (!CFCMethod_public(method)) {
             continue;
         }
 
-        if (result[0] == '\0') {
-            result = CFCUtil_cat(result, ".SS Methods inherited from ",
-                                 CFCClass_get_class_name(klass), "\n", NULL);
+        const char *class_name = CFCMethod_get_class_name(method);
+        if (strcmp(class_name, ancestor_name) != 0) {
+            // The method is implementated in a subclass and already
+            // documented.
+            continue;
         }
 
         const char *macro_sym = CFCMethod_get_macro_sym(method);
-        char *full_method_sym = CFCMethod_full_method_sym(method, NULL);
+        result = CFCUtil_cat(result, ".TP\n.BR ", macro_sym, NULL);
+        if (CFCMethod_abstract(method)) {
+            result = CFCUtil_cat(result, " \" (abstract)\"", NULL);
+        }
+        result = CFCUtil_cat(result, "\n", NULL);
+
+        char *full_sym = CFCMethod_full_method_sym(method, klass);
         char *method_man = S_man_create_func(klass, (CFCFunction*)method,
-                                             macro_sym, full_method_sym);
+                                             full_sym);
         result = CFCUtil_cat(result, method_man, NULL);
-
         FREEMEM(method_man);
-        FREEMEM(full_method_sym);
+        FREEMEM(full_sym);
     }
 
     return result;
 }
 
 static char*
-S_man_create_func(CFCClass *klass, CFCFunction *func, const char *short_sym,
-                  const char *full_sym) {
+S_man_create_func(CFCClass *klass, CFCFunction *func, const char *full_sym) {
     CFCType    *return_type   = CFCFunction_get_return_type(func);
     const char *return_type_c = CFCType_to_c(return_type);
     const char *incremented   = "";
@@ -291,18 +278,17 @@ S_man_create_func(CFCClass *klass, CFCFunction *func, 
const char *short_sym,
         incremented = " // incremented";
     }
 
-    char *param_list = S_man_create_param_list(func, full_sym);
+    char *param_list = S_man_create_param_list(klass, func);
 
     const char *pattern =
-        ".TP\n"
-        ".B %s\n"
-        ".na\n"
+        ".nf\n"
+        ".fam C\n"
         "%s%s\n"
-        ".br\n"
-        "%s"
-        ".ad\n";
-    char *result = CFCUtil_sprintf(pattern, short_sym, return_type_c,
-                                   incremented, param_list);
+        ".BR %s %s\n"
+        ".fam\n"
+        ".fi\n";
+    char *result = CFCUtil_sprintf(pattern, return_type_c, incremented,
+                                   full_sym, param_list);
 
     FREEMEM(param_list);
 
@@ -323,8 +309,8 @@ S_man_create_func(CFCClass *klass, CFCFunction *func, const 
char *short_sym,
     if (docucomment) {
         // Description
         const char *raw_desc = CFCDocuComment_get_description(docucomment);
-        char *desc = S_man_escape_content(raw_desc);
-        result = CFCUtil_cat(result, ".IP\n", desc, "\n", NULL);
+        char *desc = S_md_to_man(klass, raw_desc, true);
+        result = CFCUtil_cat(result, ".IP\n", desc, NULL);
         FREEMEM(desc);
 
         // Params
@@ -335,9 +321,9 @@ S_man_create_func(CFCClass *klass, CFCFunction *func, const 
char *short_sym,
         if (param_names[0]) {
             result = CFCUtil_cat(result, ".RS\n", NULL);
             for (size_t i = 0; param_names[i] != NULL; i++) {
-                char *doc = S_man_escape_content(param_docs[i]);
+                char *doc = S_md_to_man(klass, param_docs[i], true);
                 result = CFCUtil_cat(result, ".TP\n.I ", param_names[i],
-                                     "\n", doc, "\n", NULL);
+                                     "\n", doc, NULL);
                 FREEMEM(doc);
             }
             result = CFCUtil_cat(result, ".RE\n", NULL);
@@ -346,9 +332,8 @@ S_man_create_func(CFCClass *klass, CFCFunction *func, const 
char *short_sym,
         // Return value
         const char *retval_doc = CFCDocuComment_get_retval(docucomment);
         if (retval_doc && strlen(retval_doc)) {
-            char *doc = S_man_escape_content(retval_doc);
-            result = CFCUtil_cat(result, ".IP\n.B Returns:\n", doc, "\n",
-                                 NULL);
+            char *doc = S_md_to_man(klass, retval_doc, true);
+            result = CFCUtil_cat(result, ".IP\n.B Returns:\n", doc, NULL);
             FREEMEM(doc);
         }
     }
@@ -357,23 +342,33 @@ S_man_create_func(CFCClass *klass, CFCFunction *func, 
const char *short_sym,
 }
 
 static char*
-S_man_create_param_list(CFCFunction *func, const char *full_sym) {
+S_man_create_param_list(CFCClass *klass, CFCFunction *func) {
     CFCParamList  *param_list = CFCFunction_get_param_list(func);
     CFCVariable  **variables  = CFCParamList_get_variables(param_list);
 
     if (!variables[0]) {
-        return CFCUtil_sprintf(".BR %s (void);\n", full_sym);
+        return CFCUtil_strdup("(void);");
     }
 
-    char *result = CFCUtil_sprintf(".BR %s (", full_sym);
+    const char *cfc_class = CFCBase_get_cfc_class((CFCBase*)func);
+    int is_method = strcmp(cfc_class, "Clownfish::CFC::Model::Method") == 0;
+    char *result = CFCUtil_strdup("(");
 
     for (int i = 0; variables[i]; ++i) {
         CFCVariable *variable = variables[i];
         CFCType     *type     = CFCVariable_get_type(variable);
-        const char  *type_c   = CFCType_to_c(type);
         const char  *name     = CFCVariable_micro_sym(variable);
+        char        *type_c;
 
-        result = CFCUtil_cat(result, "\n.br\n.RB \"    ", type_c, " \" ", name,
+        if (is_method && i == 0) {
+            const char *struct_sym = CFCClass_full_struct_sym(klass);
+            type_c = CFCUtil_sprintf("%s*", struct_sym);
+        }
+        else {
+            type_c = CFCUtil_strdup(CFCType_to_c(type));
+        }
+
+        result = CFCUtil_cat(result, "\n.RB \"    ", type_c, " \" ", name,
                              NULL);
 
         if (variables[i+1] || CFCType_decremented(type)) {
@@ -381,14 +376,16 @@ S_man_create_param_list(CFCFunction *func, const char 
*full_sym) {
             if (variables[i+1]) {
                 result = CFCUtil_cat(result, ",", NULL);
             }
-            else {
+            if (CFCType_decremented(type)) {
                 result = CFCUtil_cat(result, " // decremented", NULL);
             }
             result = CFCUtil_cat(result, "\"", NULL);
         }
+
+        FREEMEM(type_c);
     }
 
-    result = CFCUtil_cat(result, "\n.br\n);\n.br\n", NULL);
+    result = CFCUtil_cat(result, "\n);", NULL);
 
     return result;
 }
@@ -413,12 +410,253 @@ S_man_create_inheritance(CFCClass *klass) {
 }
 
 static char*
-S_man_escape_content(const char *content) {
+S_md_to_man(CFCClass *klass, const char *md, int needs_indent) {
+    cmark_node *doc = cmark_parse_document(md, strlen(md));
+    char *result = S_nodes_to_man(klass, doc, needs_indent);
+    cmark_node_free(doc);
+
+    return result;
+}
+
+static char*
+S_nodes_to_man(CFCClass *klass, cmark_node *node, int needs_indent) {
+    char *result = CFCUtil_strdup("");
+    int has_indent = needs_indent;
+    int has_vspace = true;
+
+    while (node) {
+        cmark_node_type type = cmark_node_get_type(node);
+
+        switch (type) {
+            case NODE_DOCUMENT: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_man = S_nodes_to_man(klass, child,
+                                                    needs_indent);
+                result = CFCUtil_cat(result, children_man, NULL);
+                FREEMEM(children_man);
+                break;
+            }
+
+            case NODE_PARAGRAPH: {
+                if (needs_indent && !has_indent) {
+                    result = CFCUtil_cat(result, ".IP\n", NULL);
+                    has_indent = true;
+                }
+                else if (!needs_indent && has_indent) {
+                    result = CFCUtil_cat(result, ".P\n", NULL);
+                    has_indent = false;
+                }
+                else if (!has_vspace) {
+                    result = CFCUtil_cat(result, "\n", NULL);
+                }
+
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_man = S_nodes_to_man(klass, child,
+                                                    needs_indent);
+                result = CFCUtil_cat(result, children_man, "\n", NULL);
+                FREEMEM(children_man);
+
+                has_vspace = false;
+
+                break;
+            }
+
+            case NODE_BLOCK_QUOTE: {
+                if (needs_indent) {
+                    result = CFCUtil_cat(result, ".RS\n", NULL);
+                }
+
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_man = S_nodes_to_man(klass, child, true);
+                result = CFCUtil_cat(result, ".IP\n", children_man, NULL);
+                FREEMEM(children_man);
+
+                if (needs_indent) {
+                    result = CFCUtil_cat(result, ".RE\n", NULL);
+                    has_indent = false;
+                }
+                else {
+                    has_indent = true;
+                }
+
+                break;
+            }
+
+            case NODE_LIST_ITEM: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_man = S_nodes_to_man(klass, child, true);
+                result = CFCUtil_cat(result, ".IP \\(bu\n", children_man,
+                                     NULL);
+                FREEMEM(children_man);
+                break;
+            }
+
+            case NODE_LIST: {
+                if (needs_indent) {
+                    result = CFCUtil_cat(result, ".RS\n", NULL);
+                }
+
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_man = S_nodes_to_man(klass, child,
+                                                    needs_indent);
+                result = CFCUtil_cat(result, children_man, NULL);
+                FREEMEM(children_man);
+
+                if (needs_indent) {
+                    result = CFCUtil_cat(result, ".RE\n", NULL);
+                    has_indent = false;
+                }
+                else {
+                    has_indent = true;
+                }
+
+                break;
+            }
+
+            case NODE_HEADER: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_man = S_nodes_to_man(klass, child,
+                                                    needs_indent);
+                result = CFCUtil_cat(result, ".SS\n", children_man, "\n", 
NULL);
+                FREEMEM(children_man);
+                has_indent = false;
+                has_vspace = true;
+                break;
+            }
+
+            case NODE_CODE_BLOCK: {
+                if (needs_indent) {
+                    result = CFCUtil_cat(result, ".RS\n", NULL);
+                }
+
+                const char *content = cmark_node_get_string_content(node);
+                char *escaped = S_man_escape(content);
+                result = CFCUtil_cat(result, ".IP\n.nf\n.fam C\n", escaped,
+                                     ".fam\n.fi\n", NULL);
+                FREEMEM(escaped);
+
+                if (needs_indent) {
+                    result = CFCUtil_cat(result, ".RE\n", NULL);
+                    has_indent = false;
+                }
+                else {
+                    has_indent = true;
+                }
+
+                break;
+            }
+
+            case NODE_HTML:
+                CFCUtil_warn("HTML not supported in man pages");
+                break;
+
+            case NODE_HRULE:
+                break;
+
+            case NODE_REFERENCE_DEF:
+                break;
+
+            case NODE_TEXT: {
+                const char *content = cmark_node_get_string_content(node);
+                char *escaped = S_man_escape(content);
+                result = CFCUtil_cat(result, escaped, NULL);
+                FREEMEM(escaped);
+                break;
+            }
+
+            case NODE_LINEBREAK:
+                result = CFCUtil_cat(result, "\n.br\n", NULL);
+                break;
+
+            case NODE_SOFTBREAK:
+                result = CFCUtil_cat(result, "\n", NULL);
+                break;
+
+            case NODE_INLINE_CODE: {
+                const char *content = cmark_node_get_string_content(node);
+                char *escaped = S_man_escape(content);
+                result = CFCUtil_cat(result, "\\FC", escaped, "\\F[]", NULL);
+                FREEMEM(escaped);
+                break;
+            }
+
+            case NODE_INLINE_HTML: {
+                const char *html = cmark_node_get_string_content(node);
+                CFCUtil_warn("HTML not supported in man pages: %s", html);
+                break;
+            }
+
+            case NODE_LINK: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_man = S_nodes_to_man(klass, child,
+                                                    needs_indent);
+                const char *url = cmark_node_get_url(node);
+                if (CFCUri_is_clownfish_uri(url)) {
+                    if (children_man[0] != '\0') {
+                        result = CFCUtil_cat(result, children_man, NULL);
+                    }
+                    else {
+                        CFCUri *uri_obj = CFCUri_new(url, klass);
+                        char *link_text = CFCC_link_text(uri_obj, klass);
+                        if (link_text) {
+                            result = CFCUtil_cat(result, link_text, NULL);
+                            FREEMEM(link_text);
+                        }
+                        CFCBase_decref((CFCBase*)uri_obj);
+                    }
+                }
+                else {
+                    result = CFCUtil_cat(result, "\n.UR ", url, "\n",
+                                         children_man, "\n.UE\n",
+                                         NULL);
+                }
+                FREEMEM(children_man);
+                break;
+            }
+
+            case NODE_IMAGE:
+                CFCUtil_warn("Images not supported in man pages");
+                break;
+
+            case NODE_STRONG: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_man = S_nodes_to_man(klass, child,
+                                                    needs_indent);
+                result = CFCUtil_cat(result, "\\fB", children_man, "\\f[]",
+                                     NULL);
+                FREEMEM(children_man);
+                break;
+            }
+
+            case NODE_EMPH: {
+                cmark_node *child = cmark_node_first_child(node);
+                char *children_man = S_nodes_to_man(klass, child,
+                                                    needs_indent);
+                result = CFCUtil_cat(result, "\\fI", children_man, "\\f[]",
+                                     NULL);
+                FREEMEM(children_man);
+                break;
+            }
+
+            default:
+                CFCUtil_die("Invalid cmark node type: %d", type);
+                break;
+        }
+
+        node = cmark_node_next(node);
+    }
+
+    return result;
+}
+
+static char*
+S_man_escape(const char *content) {
+    size_t  len        = strlen(content);
     size_t  result_len = 0;
-    size_t  result_cap = strlen(content) + 256;
+    size_t  result_cap = len + 256;
     char   *result     = (char*)MALLOCATE(result_cap + 1);
 
-    for (size_t i = 0; content[i]; i++) {
+    for (size_t i = 0; i < len; i++) {
         const char *subst      = content + i;
         size_t      subst_size = 1;
 
@@ -433,36 +671,18 @@ S_man_escape_content(const char *content) {
                 subst      = "\\-";
                 subst_size = 2;
                 break;
-            case '\n':
-                // Escape dot after newline.
-                if (content[i+1] == '.') {
-                    subst      = "\n\\";
-                    subst_size = 2;
-                }
-                break;
-            case '<':
-                // <code> markup.
-                if (strncmp(content + i + 1, "code>", 5) == 0) {
-                    subst      = "\\fI";
+            case '.':
+                // Escape dot at start of line.
+                if (i == 0 || content[i-1] == '\n') {
+                    subst      = "\\&.";
                     subst_size = 3;
-                    i += 5;
-                }
-                else if (strncmp(content + i + 1, "/code>", 6) == 0) {
-                    subst      = "\\fP";
-                    subst_size = 3;
-                    i += 6;
                 }
                 break;
-            case 'L':
-                if (content[i+1] == '<') {
-                    // POD-style link.
-                    struct CFCPodLink pod_link;
-                    S_parse_pod_link(content + i + 2, &pod_link);
-                    if (pod_link.total_size) {
-                        subst      = pod_link.text;
-                        subst_size = pod_link.text_size;
-                        i += pod_link.total_size + 1;
-                    }
+            case '\'':
+                // Escape single quote at start of line.
+                if (i == 0 || content[i-1] == '\n') {
+                    subst      = "\\&'";
+                    subst_size = 3;
                 }
                 break;
             default:
@@ -483,32 +703,3 @@ S_man_escape_content(const char *content) {
     return result;
 }
 
-// Quick and dirty parsing of POD links. The syntax isn't fully supported
-// and the result isn't man-escaped. But it should be good enough for now
-// since at some point we'll switch to another format anyway.
-static void
-S_parse_pod_link(const char *content, CFCPodLink *pod_link) {
-    int in_text = true;
-
-    for (size_t i = 0; i < 256 && content[i]; ++i) {
-        if (content[i] == '|') {
-            if (in_text) {
-                pod_link->text_size = i;
-                in_text = false;
-            }
-        }
-        else if (content[i] == '>') {
-            pod_link->total_size = i + 1;
-            pod_link->text       = content;
-            if (in_text) {
-                pod_link->text_size = i;
-            }
-            return;
-        }
-    }
-
-    pod_link->total_size = 0;
-    pod_link->text       = NULL;
-    pod_link->text_size  = 0;
-}
-

Reply via email to