Generate man pages for C bindings
Project: http://git-wip-us.apache.org/repos/asf/lucy/repo Commit: http://git-wip-us.apache.org/repos/asf/lucy/commit/05d6117d Tree: http://git-wip-us.apache.org/repos/asf/lucy/tree/05d6117d Diff: http://git-wip-us.apache.org/repos/asf/lucy/diff/05d6117d Branch: refs/heads/master Commit: 05d6117d9e91b32c21450569d90ce2c515729769 Parents: c0e8814 Author: Nick Wellnhofer <[email protected]> Authored: Sun Dec 23 16:39:57 2012 +0100 Committer: Nick Wellnhofer <[email protected]> Committed: Sat Mar 9 17:51:54 2013 +0100 ---------------------------------------------------------------------- clownfish/compiler/c/cfc.c | 1 + clownfish/compiler/src/CFCC.c | 54 ++++++ clownfish/compiler/src/CFCC.h | 5 + clownfish/compiler/src/CFCCClass.c | 283 +++++++++++++++++++++++++++++++ clownfish/compiler/src/CFCCClass.h | 5 + 5 files changed, 348 insertions(+), 0 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucy/blob/05d6117d/clownfish/compiler/c/cfc.c ---------------------------------------------------------------------- diff --git a/clownfish/compiler/c/cfc.c b/clownfish/compiler/c/cfc.c index 9fa27f8..70bb820 100644 --- a/clownfish/compiler/c/cfc.c +++ b/clownfish/compiler/c/cfc.c @@ -188,6 +188,7 @@ main(int argc, char **argv) { c_binding = CFCC_new(hierarchy, header, footer); CFCC_write_callbacks(c_binding); + CFCC_write_man_pages(c_binding); CFCBase_decref((CFCBase*)c_binding); CFCBase_decref((CFCBase*)core_binding); http://git-wip-us.apache.org/repos/asf/lucy/blob/05d6117d/clownfish/compiler/src/CFCC.c ---------------------------------------------------------------------- diff --git a/clownfish/compiler/src/CFCC.c b/clownfish/compiler/src/CFCC.c index 0a1b9cb..6b62f69 100644 --- a/clownfish/compiler/src/CFCC.c +++ b/clownfish/compiler/src/CFCC.c @@ -113,4 +113,58 @@ CFCC_write_callbacks(CFCC *self) { FREEMEM(file_content); } +void +CFCC_write_man_pages(CFCC *self) { + CFCHierarchy *hierarchy = self->hierarchy; + CFCClass **ordered = CFCHierarchy_ordered_classes(hierarchy); + + size_t num_classes = 0; + for (size_t i = 0; ordered[i] != NULL; i++) { + CFCClass *klass = ordered[i]; + if (!CFCClass_included(klass)) { ++num_classes; } + } + char **man_pages = (char**)CALLOCATE(num_classes, sizeof(char*)); + + // Generate man pages, but don't write. That way, if there's an error + // while generating the pages, we leak memory but don't clutter up the file + // system. + for (size_t i = 0, j = 0; ordered[i] != NULL; i++) { + CFCClass *klass = ordered[i]; + if (CFCClass_included(klass)) { continue; } + + char *man_page = CFCCClass_create_man_page(klass); + man_pages[j++] = man_page; + } + + const char *dest = CFCHierarchy_get_dest(hierarchy); + char *man3_path + = CFCUtil_sprintf("%s" CHY_DIR_SEP "man" CHY_DIR_SEP "man3", dest); + if (!CFCUtil_is_dir(man3_path)) { + CFCUtil_make_path(man3_path); + if (!CFCUtil_is_dir(man3_path)) { + CFCUtil_die("Can't make path %s", man3_path); + } + } + + // Write out any man pages that have changed. + for (size_t i = 0, j = 0; ordered[i] != NULL; i++) { + CFCClass *klass = ordered[i]; + if (CFCClass_included(klass)) { continue; } + + char *man_page = man_pages[j++]; + if (!man_page) { continue; } + + const char *class_name = CFCClass_get_class_name(klass); + char *filename + = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s.3", man3_path, class_name); + CFCUtil_write_if_changed(filename, man_page, strlen(man_page)); + FREEMEM(filename); + FREEMEM(man_page); + } + + FREEMEM(man3_path); + FREEMEM(man_pages); + FREEMEM(ordered); +} + http://git-wip-us.apache.org/repos/asf/lucy/blob/05d6117d/clownfish/compiler/src/CFCC.h ---------------------------------------------------------------------- diff --git a/clownfish/compiler/src/CFCC.h b/clownfish/compiler/src/CFCC.h index 694b4e7..e66a900 100644 --- a/clownfish/compiler/src/CFCC.h +++ b/clownfish/compiler/src/CFCC.h @@ -49,6 +49,11 @@ CFCC_destroy(CFCC *self); void CFCC_write_callbacks(CFCC *self); +/** Write all man pages. + */ +void +CFCC_write_man_pages(CFCC *self); + #ifdef __cplusplus } #endif http://git-wip-us.apache.org/repos/asf/lucy/blob/05d6117d/clownfish/compiler/src/CFCCClass.c ---------------------------------------------------------------------- diff --git a/clownfish/compiler/src/CFCCClass.c b/clownfish/compiler/src/CFCCClass.c index b694ba7..7b65530 100644 --- a/clownfish/compiler/src/CFCCClass.c +++ b/clownfish/compiler/src/CFCCClass.c @@ -14,11 +14,36 @@ * limitations under the License. */ +#include <string.h> + #include "CFCCClass.h" #include "CFCClass.h" +#include "CFCDocuComment.h" +#include "CFCFunction.h" #include "CFCMethod.h" +#include "CFCParamList.h" +#include "CFCType.h" #include "CFCUtil.h" +static char* +S_man_create_synopsis(CFCClass *klass); + +static char* +S_man_create_functions(CFCClass *klass); + +static char* +S_man_create_methods(CFCClass *klass); + +static char* +S_man_create_func(CFCClass *klass, CFCFunction *func, const char *short_sym, + const char *full_sym); + +static char* +S_man_create_inheritance(CFCClass *klass); + +static char* +S_man_escape_content(const char *content); + // Declare dummy host callbacks. char* CFCCClass_callback_decs(CFCClass *klass) { @@ -41,3 +66,261 @@ CFCCClass_callback_decs(CFCClass *klass) { return cb_decs; } +char* +CFCCClass_create_man_page(CFCClass *klass) { + const char *class_name = CFCClass_get_class_name(klass); + + CFCDocuComment *docucom = CFCClass_get_docucomment(klass); + if (!docucom) { return NULL; } + + // Get the class's brief description. + const char *raw_brief = CFCDocuComment_get_brief(docucom); + char *brief = S_man_escape_content(raw_brief); + + // Get the class's long description. + const char *raw_description = CFCDocuComment_get_long(docucom); + char *description = S_man_escape_content(raw_description); + + // Create SYNOPSIS. + char *synopsis = S_man_create_synopsis(klass); + + // Create CONSTRUCTORS. + char *functions_man = S_man_create_functions(klass); + + // Create METHODS, possibly including an ABSTRACT METHODS section. + char *methods_man = S_man_create_methods(klass); + + // Build an INHERITANCE section describing class ancestry. + char *inheritance = S_man_create_inheritance(klass); + + // Put it all together. + const char pattern[] = + ".\\\" Licensed to the Apache Software Foundation (ASF) under one or more\n" + ".\\\" contributor license agreements. See the NOTICE file distributed with\n" + ".\\\" this work for additional information regarding copyright ownership.\n" + ".\\\" The ASF licenses this file to You under the Apache License, Version 2.0\n" + ".\\\" (the \"License\"); you may not use this file except in compliance with\n" + ".\\\" the License. You may obtain a copy of the License at\n" + ".\\\"\n" + ".\\\" http://www.apache.org/licenses/LICENSE-2.0\n" + ".\\\"\n" + ".\\\" Unless required by applicable law or agreed to in writing, software\n" + ".\\\" distributed under the License is distributed on an \"AS IS\" BASIS,\n" + ".\\\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + ".\\\" See the License for the specific language governing permissions and\n" + ".\\\" limitations under the License.\n" + ".TH %s 3\n" + ".SH NAME\n" + "%s - %s\n" + "%s" + ".SH DESCRIPTION\n" + "%s\n" + "%s" + "%s" + "%s"; + char *man_page + = CFCUtil_sprintf(pattern, class_name, class_name, brief, synopsis, + description, functions_man, methods_man, + inheritance); + + FREEMEM(brief); + FREEMEM(synopsis); + FREEMEM(description); + FREEMEM(functions_man); + FREEMEM(methods_man); + FREEMEM(inheritance); + + return man_page; +} + +static char* +S_man_create_synopsis(CFCClass *klass) { + return CFCUtil_strdup(""); +} + +static char* +S_man_create_functions(CFCClass *klass) { + CFCFunction **functions = CFCClass_functions(klass); + const char *class_name = CFCClass_get_class_name(klass); + char *result = CFCUtil_strdup(".SH FUNCTIONS\n"); + + for (int func_num = 0; functions[func_num] != NULL; func_num++) { + CFCFunction *func = functions[func_num]; + if (!CFCFunction_public(func)) { continue; } + + const char *micro_sym = CFCFunction_micro_sym(func); + const char *full_func_sym = CFCFunction_full_func_sym(func); + + char *redman = S_man_create_func(klass, func, micro_sym, + full_func_sym); + result = CFCUtil_cat(result, redman, NULL); + FREEMEM(redman); + } + + return result; +} + +static char* +S_man_create_methods(CFCClass *klass) { + CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); + const char *class_name = CFCClass_get_class_name(klass); + char *result = CFCUtil_strdup(".SH METHODS\n"); + + for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { + CFCMethod *method = fresh_methods[meth_num]; + if (!CFCMethod_public(method)) { continue; } + + 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); + result = CFCUtil_cat(result, method_man, NULL); + + FREEMEM(method_man); + FREEMEM(full_method_sym); + } + + FREEMEM(fresh_methods); + + return result; +} + +static char* +S_man_create_func(CFCClass *klass, CFCFunction *func, const char *short_sym, + const char *full_sym) { + CFCType *return_type + = CFCFunction_get_return_type(func); + const char *return_type_c = CFCType_to_c(return_type); + + CFCParamList *param_list + = CFCFunction_get_param_list(func); + const char *param_list_c = CFCParamList_to_c(param_list); + + const char *pattern = + ".TP\n" + ".B %s\n" + ".na\n" + "%s\n" + ".br\n" + ".BR \"%s\" \"(%s);\"\n" + ".ad\n"; + char *result = CFCUtil_sprintf(pattern, short_sym, return_type_c, full_sym, + param_list_c); + + // Get documentation, which may be inherited. + CFCDocuComment *docucomment = CFCFunction_get_docucomment(func); + if (!docucomment) { + const char *micro_sym = CFCFunction_micro_sym(func); + CFCClass *parent = klass; + while (NULL != (parent = CFCClass_get_parent(parent))) { + CFCFunction *parent_func + = (CFCFunction*)CFCClass_method(parent, micro_sym); + if (!parent_func) { break; } + docucomment = CFCFunction_get_docucomment(parent_func); + if (docucomment) { break; } + } + } + + 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); + FREEMEM(desc); + + // Params + const char **param_names + = CFCDocuComment_get_param_names(docucomment); + const char **param_docs + = CFCDocuComment_get_param_docs(docucomment); + 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]); + result = CFCUtil_cat(result, ".TP\n.I ", param_names[i], + "\n", doc, "\n", NULL); + FREEMEM(doc); + } + result = CFCUtil_cat(result, ".RE\n", NULL); + } + + // 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); + FREEMEM(doc); + } + } + + return result; +} + +static char* +S_man_create_inheritance(CFCClass *klass) { + CFCClass *ancestor = CFCClass_get_parent(klass); + char *result = CFCUtil_strdup(""); + + if (!ancestor) { return result; } + + const char *class_name = CFCClass_get_class_name(klass); + result = CFCUtil_cat(result, ".SH INHERITANCE\n", class_name, NULL); + while (ancestor) { + const char *ancestor_name = CFCClass_get_class_name(ancestor); + result = CFCUtil_cat(result, " is a ", ancestor_name, NULL); + ancestor = CFCClass_get_parent(ancestor); + } + result = CFCUtil_cat(result, ".\n", NULL); + + return result; +} + +static char* +S_man_escape_content(const char *content) { + size_t result_len = 0; + size_t result_cap = strlen(content) + 20; + char *result = (char*)MALLOCATE(result_cap + 1); + int fill = 1; + + for (size_t i = 0; content[i]; i++) { + char c[8]; + int num_chars = 1; + + c[0] = content[i]; + + switch (c[0]) { + case '\\': + c[1] = 'e'; + num_chars = 2; + break; + case '-': + c[0] = '\\'; + c[1] = '-'; + num_chars = 2; + break; + case '\n': + if (content[i+1] == '.') { + memcpy(c + num_chars, "\\&", 2); + num_chars += 2; + } + break; + default: + break; + } + + if (result_len + num_chars > result_cap) { + result_cap += 50; + result = (char*)REALLOCATE(result, result_cap + 1); + } + + memcpy(result + result_len, c, num_chars); + result_len += num_chars; + } + + result[result_len] = '\0'; + + return result; +} + http://git-wip-us.apache.org/repos/asf/lucy/blob/05d6117d/clownfish/compiler/src/CFCCClass.h ---------------------------------------------------------------------- diff --git a/clownfish/compiler/src/CFCCClass.h b/clownfish/compiler/src/CFCCClass.h index c701d2f..b319596 100644 --- a/clownfish/compiler/src/CFCCClass.h +++ b/clownfish/compiler/src/CFCCClass.h @@ -28,6 +28,11 @@ struct CFCClass; char* CFCCClass_callback_decs(struct CFCClass *klass); +/** Return the man page for the class. + */ +char* +CFCCClass_create_man_page(struct CFCClass *klass); + #ifdef __cplusplus } #endif
