static void S_set_error(CFCUri *self, const char *error) { self->type = CFC_URI_ERROR; self->error = CFCUtil_sprintf("%s: %s", error, self->string); CFCUtil_warn(self->error); }
static CFCClass* S_class_by_struct_sym_prereq(CFCParcel *self, const char *struct_sym, size_t prefix_len) { CFCClass *klass = S_class_by_struct_sym(self, struct_sym, prefix_len); if (klass && prefix_len != 0) { return klass; } for (size_t i = 0; self->prereqs[i]; ++i) { const char *prereq_name = CFCPrereq_get_name(self->prereqs[i]); CFCParcel *prereq_parcel = CFCParcel_fetch(prereq_name); CFCClass *candidate = S_class_by_struct_sym(prereq_parcel, struct_sym, prefix_len); if (candidate) { if (prefix_len != 0) { return candidate; } if (klass) { CFCUtil_warn("Type '%s' is ambiguous. Do you mean %s or %s?", struct_sym, CFCClass_full_struct_sym(klass), CFCClass_full_struct_sym(candidate)); return NULL; } klass = candidate; } } return klass; }
// Convert a single node. static char* S_node_to_pod(cmark_node *node, CFCClass *klass, int header_level) { char *result = CFCUtil_strdup(""); if (node == NULL) { return result; } int found_matching_code_block = false; cmark_iter *iter = cmark_iter_new(node); cmark_event_type ev_type; while (CMARK_EVENT_DONE != (ev_type = cmark_iter_next(iter))) { cmark_node *node = cmark_iter_get_node(iter); cmark_node_type type = cmark_node_get_type(node); switch (type) { case CMARK_NODE_DOCUMENT: break; case CMARK_NODE_PARAGRAPH: if (ev_type == CMARK_EVENT_EXIT) { result = CFCUtil_cat(result, "\n\n", NULL); } break; case CMARK_NODE_BLOCK_QUOTE: case CMARK_NODE_LIST: if (ev_type == CMARK_EVENT_ENTER) { result = CFCUtil_cat(result, "=over\n\n", NULL); } else { result = CFCUtil_cat(result, "=back\n\n", NULL); } break; case CMARK_NODE_ITEM: // TODO: Ordered lists. if (ev_type == CMARK_EVENT_ENTER) { result = CFCUtil_cat(result, "=item *\n\n", NULL); } break; case CMARK_NODE_HEADER: if (ev_type == CMARK_EVENT_ENTER) { int extra_level = cmark_node_get_header_level(node) - 1; char *header = CFCUtil_sprintf("=head%d ", header_level + extra_level); result = CFCUtil_cat(result, header, NULL); FREEMEM(header); } else { result = CFCUtil_cat(result, "\n\n", NULL); } break; case CMARK_NODE_CODE_BLOCK: { int is_host = CFCMarkdown_code_block_is_host(node, "perl"); if (is_host) { found_matching_code_block = true; const char *content = cmark_node_get_literal(node); char *copy = CFCUtil_strdup(content); // Chomp trailing newline. size_t len = strlen(copy); if (len > 0 && copy[len-1] == '\n') { copy[len-1] = '\0'; } char *indented = CFCUtil_global_replace(copy, "\n", "\n "); result = CFCUtil_cat(result, " ", indented, "\n\n", NULL); FREEMEM(indented); FREEMEM(copy); } if (CFCMarkdown_code_block_is_last(node)) { if (!found_matching_code_block) { result = CFCUtil_cat(result, " Code example for Perl is missing\n\n"); } else { // Reset. found_matching_code_block = false; } } break; } case CMARK_NODE_HTML: { const char *html = cmark_node_get_literal(node); result = CFCUtil_cat(result, "=begin html\n\n", html, "\n=end\n\n", NULL); break; } case CMARK_NODE_HRULE: break; case CMARK_NODE_TEXT: { const char *content = cmark_node_get_literal(node); char *escaped = S_pod_escape(content); result = CFCUtil_cat(result, escaped, NULL); FREEMEM(escaped); break; } case CMARK_NODE_LINEBREAK: // POD doesn't support line breaks. Start a new paragraph. result = CFCUtil_cat(result, "\n\n", NULL); break; case CMARK_NODE_SOFTBREAK: result = CFCUtil_cat(result, "\n", NULL); break; case CMARK_NODE_CODE: { const char *content = cmark_node_get_literal(node); char *escaped = S_pod_escape(content); result = CFCUtil_cat(result, "C<", escaped, ">", NULL); FREEMEM(escaped); break; } case CMARK_NODE_INLINE_HTML: { const char *html = cmark_node_get_literal(node); CFCUtil_warn("Inline HTML not supported in POD: %s", html); break; } case CMARK_NODE_LINK: if (ev_type == CMARK_EVENT_ENTER) { char *pod = S_convert_link(node, klass, header_level); result = CFCUtil_cat(result, pod, NULL); FREEMEM(pod); cmark_iter_reset(iter, node, CMARK_EVENT_EXIT); } break; case CMARK_NODE_IMAGE: CFCUtil_warn("Images not supported in POD"); break; case CMARK_NODE_STRONG: if (ev_type == CMARK_EVENT_ENTER) { result = CFCUtil_cat(result, "B<", NULL); } else { result = CFCUtil_cat(result, ">", NULL); } break; case CMARK_NODE_EMPH: if (ev_type == CMARK_EVENT_ENTER) { result = CFCUtil_cat(result, "I<", NULL); } else { result = CFCUtil_cat(result, ">", NULL); } break; default: CFCUtil_die("Invalid cmark node type: %d", (int)type); break; } } cmark_iter_free(iter); return result; }
static void S_resolve(CFCUri *self, const char *parcel, const char *struct_sym, const char *callable) { // Try to find a CFCClass. CFCClass *doc_class = self->doc_class; CFCClass *klass = NULL; if (parcel) { char *full_struct_sym = CFCUtil_sprintf("%s_%s", parcel, struct_sym); klass = CFCClass_fetch_by_struct_sym(full_struct_sym); FREEMEM(full_struct_sym); } else if (struct_sym && doc_class) { const char *prefix = CFCClass_get_prefix(doc_class); char *full_struct_sym = CFCUtil_sprintf("%s%s", prefix, struct_sym); klass = CFCClass_fetch_by_struct_sym(full_struct_sym); FREEMEM(full_struct_sym); } else if (callable) { klass = doc_class; } if (klass) { if (!CFCClass_public(klass)) { CFCUtil_warn("Non-public class '%s' in Clownfish URI: %s", CFCClass_get_struct_sym(klass), self->string); } self->type = CFC_URI_CLASS; self->klass = klass; CFCBase_incref((CFCBase*)klass); if (callable) { if (islower(callable[0])) { CFCFunction *function = CFCClass_function(klass, callable); if (!function) { CFCUtil_warn("Unknown function '%s' in Clownfish URI: %s", callable, self->string); } else if (!CFCFunction_public(function)) { CFCUtil_warn("Non-public function '%s' in Clownfish URI:" " %s", callable, self->string); } self->type = CFC_URI_FUNCTION; self->callable = CFCUtil_strdup(callable); } else { CFCMethod *method = CFCClass_method(klass, callable); if (!method) { CFCUtil_warn("Unknown method '%s' in Clownfish URI: %s", callable, self->string); } else if (!CFCMethod_public(method)) { CFCUtil_warn("Non-public method '%s' in Clownfish URI:" " %s", callable, self->string); } self->type = CFC_URI_METHOD; self->callable = CFCUtil_strdup(callable); } } return; } // Try to find a CFCDocument. if (!parcel && struct_sym && !callable) { CFCDocument *doc = CFCDocument_fetch(struct_sym); if (doc) { self->type = CFC_URI_DOCUMENT; self->document = doc; CFCBase_incref((CFCBase*)doc); return; } } S_set_error(self, "Couldn't resolve Clownfish URI"); }
static char* S_convert_link(CFCClass *klass, cmark_node *link) { cmark_node *child = cmark_node_first_child(link); const char *uri = cmark_node_get_url(link); char *text = S_nodes_to_pod(klass, child); char *retval; if (!CFCUri_is_clownfish_uri(uri)) { retval = S_pod_link(text, uri); FREEMEM(text); return retval; } char *new_uri = NULL; char *new_text = NULL; CFCUri *uri_obj = CFCUri_new(uri, klass); int type = CFCUri_get_type(uri_obj); switch (type) { case CFC_URI_NULL: // Change all instances of NULL to 'undef' new_text = CFCUtil_strdup("undef"); break; case CFC_URI_CLASS: { const char *full_struct_sym = CFCUri_full_struct_sym(uri_obj); CFCClass *uri_class = CFCClass_fetch_by_struct_sym(full_struct_sym); if (!uri_class) { CFCUtil_warn("URI class not found: %s", full_struct_sym); } else if (uri_class != klass) { const char *class_name = CFCClass_get_class_name(uri_class); new_uri = CFCUtil_strdup(class_name); } if (text[0] != '\0') { // Keep text. break; } if (strcmp(CFCUri_get_prefix(uri_obj), CFCClass_get_prefix(klass)) == 0 ) { // Same parcel. const char *struct_sym = CFCUri_get_struct_sym(uri_obj); new_text = CFCUtil_strdup(struct_sym); } else { // Other parcel. if (!uri_class) { new_text = CFCUtil_strdup(full_struct_sym); } else { const char *class_name = CFCClass_get_class_name(uri_class); new_text = CFCUtil_strdup(class_name); } } break; } case CFC_URI_FUNCTION: case CFC_URI_METHOD: { const char *full_struct_sym = CFCUri_full_struct_sym(uri_obj); const char *func_sym = CFCUri_get_func_sym(uri_obj); // Convert "Err_get_error" to "Clownfish->error". if (strcmp(full_struct_sym, "cfish_Err") == 0 && strcmp(func_sym, "get_error") == 0 ) { new_text = CFCUtil_strdup("Clownfish->error"); break; } CFCClass *uri_class = CFCClass_fetch_by_struct_sym(full_struct_sym); // TODO: Link to relevant POD section. This isn't easy because // the section headers for functions also contain a description // of the parameters. if (!uri_class) { CFCUtil_warn("URI class not found: %s", full_struct_sym); } else if (uri_class != klass) { const char *class_name = CFCClass_get_class_name(uri_class); new_uri = CFCUtil_strdup(class_name); } new_text = CFCUtil_sprintf("%s()", func_sym); for (size_t i = 0; new_text[i] != '\0'; ++i) { new_text[i] = tolower(new_text[i]); } break; } } if (new_text) { FREEMEM(text); text = new_text; } if (new_uri) { retval = S_pod_link(text, new_uri); FREEMEM(new_uri); FREEMEM(text); } else { retval = text; } CFCBase_decref((CFCBase*)uri_obj); return retval; }
char* CFCC_link_text(CFCUri *uri_obj, CFCClass *klass) { char *link_text = NULL; int type = CFCUri_get_type(uri_obj); switch (type) { case CFC_URI_CLASS: { if (strcmp(CFCUri_get_prefix(uri_obj), CFCClass_get_prefix(klass)) == 0 ) { // Same parcel. const char *struct_sym = CFCUri_get_struct_sym(uri_obj); link_text = CFCUtil_strdup(struct_sym); } else { // Other parcel. const char *full_struct_sym = CFCUri_full_struct_sym(uri_obj); CFCClass *uri_class = CFCClass_fetch_by_struct_sym(full_struct_sym); if (!uri_class) { CFCUtil_warn("URI class not found: %s", full_struct_sym); } else { const char *class_name = CFCClass_get_class_name(uri_class); link_text = CFCUtil_strdup(class_name); } } break; } case CFC_URI_FUNCTION: case CFC_URI_METHOD: { #if 1 const char *func_sym = CFCUri_get_func_sym(uri_obj); link_text = CFCUtil_sprintf("%s()", func_sym); #else // Full function sym. const char *full_struct_sym = CFCUri_full_struct_sym(uri_obj); const char *func_sym = CFCUri_get_func_sym(uri_obj); if (strcmp(full_struct_sym, CFCClass_full_struct_sym(klass)) == 0 ) { // Same class. link_text = CFCUtil_sprintf("%s()", func_sym); } else { CFCClass *uri_class = CFCClass_fetch_by_struct_sym(full_struct_sym); if (!uri_class) { CFCUtil_warn("URI class not found: %s", full_struct_sym); link_text = CFCUtil_sprintf("%s()", func_sym); } else { const char *prefix = CFCUri_get_prefix(uri_obj); const char *nickname = CFCClass_get_nickname(uri_class); if (strcmp(prefix, CFCClass_get_prefix(klass)) == 0) { // Same parcel. link_text = CFCUtil_sprintf("%s_%s()", nickname, func_sym); } else { // Other parcel. link_text = CFCUtil_sprintf("%s%s_%s()", prefix, nickname, func_sym); } } } #endif break; } } return link_text; }