int doc_display_help_aux(cptr file_name, cptr topic, rect_t display) { int rc = _OK; FILE *fp = NULL; char path[1024]; char caption[1024]; doc_ptr doc = NULL; int top = 0; /* Check for file_name#topic from a lazy client */ if (!topic) { cptr pos = strchr(file_name, '#'); if (pos) { string_ptr name = string_copy_sn(file_name, pos - file_name); int result = doc_display_help_aux(string_buffer(name), pos + 1, display); string_free(name); return result; } } sprintf(caption, "Help file '%s'", file_name); path_build(path, sizeof(path), ANGBAND_DIR_HELP, file_name); fp = my_fopen(path, "r"); if (!fp) { cmsg_format(TERM_VIOLET, "Cannot open '%s'.", file_name); msg_print(NULL); return _OK; } doc = doc_alloc(MIN(80, display.cx)); doc_read_file(doc, fp); my_fclose(fp); if (topic) { doc_pos_t pos = doc_find_bookmark(doc, topic); if (doc_pos_is_valid(pos)) top = pos.y; } rc = doc_display_aux(doc, caption, top, display); doc_free(doc); return rc; }
vec_ptr string_split(string_ptr str, char sep) { vec_ptr v = vec_alloc((vec_free_f)string_free); const char *pos = str->buf; int done = 0; while (!done && *pos) { const char *next = strchr(pos, sep); string_ptr s; if (!next && *pos) { next = strchr(pos, '\0'); assert(next); done = 1; } s = string_copy_sn(pos, next - pos); vec_add(v, s); pos = next + 1; } return v; }
string_ptr string_copy_s(const char *val) { if (!val) val = ""; return string_copy_sn(val, strlen(val)); }
string_ptr string_copy(string_ptr str) { return string_copy_sn(str->buf, str->len); }
string_ptr substring_copy(substring_ptr ss) { return string_copy_sn(substring_buffer(ss), ss->len); }
string_ptr string_alloc(void) { return string_copy_sn("", 0); }
static void _doc_process_tag(doc_ptr doc, doc_tag_ptr tag) { if ( tag->type == DOC_TAG_CLOSE_COLOR || tag->type == DOC_TAG_CLOSE_STYLE || tag->type == DOC_TAG_CLOSE_INDENT ) { doc_pop_style(doc); } else if (tag->type == DOC_TAG_COLOR) { assert(tag->arg); if (tag->arg_size == 1) { if (tag->arg[0] == '*') doc_pop_style(doc); else { doc_style_t style = *doc_current_style(doc); /* copy */ switch (tag->arg[0]) { case 'd': style.color = TERM_DARK; break; case 'w': style.color = TERM_WHITE; break; case 's': style.color = TERM_SLATE; break; case 'o': style.color = TERM_ORANGE; break; case 'r': style.color = TERM_RED; break; case 'g': style.color = TERM_GREEN; break; case 'b': style.color = TERM_BLUE; break; case 'u': style.color = TERM_UMBER; break; case 'D': style.color = TERM_L_DARK; break; case 'W': style.color = TERM_L_WHITE; break; case 'v': style.color = TERM_VIOLET; break; case 'y': style.color = TERM_YELLOW; break; case 'R': style.color = TERM_L_RED; break; case 'G': style.color = TERM_L_GREEN; break; case 'B': style.color = TERM_L_BLUE; break; case 'U': style.color = TERM_L_UMBER; break; } doc_push_style(doc, &style); } } else { string_ptr arg = string_copy_sn(tag->arg, tag->arg_size); doc_style_t style = *doc_current_style(doc); /* copy */ doc_style_f f = _get_doc_style_f(doc, string_buffer(arg)); if (f) f(&style); {/* We don't copy the named style, just its color. */ /* Also, we damn well better push a style or we'll be upset when the good little user pops! */ doc_style_t copy = *doc_current_style(doc); copy.color = style.color; doc_push_style(doc, ©); } string_free(arg); } } else if (tag->type == DOC_TAG_INDENT) { doc_style_t style = *doc_current_style(doc); style.left = doc->cursor.x; doc_push_style(doc, &style); } else { string_ptr arg = string_copy_sn(tag->arg, tag->arg_size); switch (tag->type) { case DOC_TAG_STYLE: if (tag->arg_size == 1 && tag->arg[0] == '*') doc_pop_style(doc); else {/* Better silently add one if name doesn't exist ... */ doc_style_t copy = *doc_current_style(doc); doc_style_f f = _get_doc_style_f(doc, string_buffer(arg)); if (f) f(©); doc_push_style(doc, ©); } break; case DOC_TAG_VAR: _doc_process_var(doc, string_buffer(arg)); break; case DOC_TAG_TAB: { int pos = atoi(string_buffer(arg)) + doc_current_style(doc)->left; if (pos > doc->cursor.x) doc_insert_space(doc, pos - doc->cursor.x); else doc_rollback(doc, doc_pos_create(pos, doc->cursor.y)); break; } case DOC_TAG_TOPIC: { doc_bookmark_ptr mark = malloc(sizeof(doc_bookmark_t)); mark->name = arg; /* steal ownership */ arg = NULL; mark->pos = doc->cursor; vec_add(doc->bookmarks, mark); break; } case DOC_TAG_LINK: { doc_link_ptr link = malloc(sizeof(doc_link_t)); int split = string_chr(arg, 0, '#'); int ch = 'a' + int_map_count(doc->links); if (split >= 0) { substring_t left = string_left(arg, split); substring_t right = string_right(arg, string_length(arg) - split - 1); link->file = substring_copy(&left); link->topic = substring_copy(&right); } else { link->file = arg; /* steal ownership */ arg = NULL; link->topic = NULL; } link->location.start = doc->cursor; int_map_add(doc->links, ch, link); { /* TODO: This is flawed. Here's a real world example: "(see below <link:birth.txt#PrimaryStats>)." Can you see the problem? We might line break after "[a]" right before ").". Instead, "[a])." should be treated as the current word. To fix this, we'll need a parser with a token queue that we can push onto, but this raises storage issues. */ string_ptr s = string_alloc_format("<style:link>[%c]</style>", ch); doc_insert(doc, string_buffer(s)); string_free(s); link->location.stop = doc->cursor; } break; } } string_free(arg); } }
static void _doc_write_html_file(doc_ptr doc, FILE *fp) { doc_pos_t pos; doc_char_ptr cell; byte old_a = _INVALID_COLOR; int bookmark_idx = 0; doc_bookmark_ptr next_bookmark = NULL; vec_ptr links = doc_get_links(doc); int link_idx = 0; doc_link_ptr next_link = NULL; if (bookmark_idx < vec_length(doc->bookmarks)) next_bookmark = vec_get(doc->bookmarks, bookmark_idx); if (link_idx < vec_length(links)) next_link = vec_get(links, link_idx); fprintf(fp, "<!DOCTYPE html>\n<html>\n"); if (string_length(doc->html_header)) fprintf(fp, "%s\n", string_buffer(doc->html_header)); fprintf(fp, "<body text=\"#ffffff\" bgcolor=\"#000000\"><pre>\n"); for (pos.y = 0; pos.y <= doc->cursor.y; pos.y++) { int cx = doc->width; pos.x = 0; if (pos.y == doc->cursor.y) cx = doc->cursor.x; cell = doc_char(doc, pos); if (next_bookmark && pos.y == next_bookmark->pos.y) { fprintf(fp, "<a name=\"%s\"></a>", string_buffer(next_bookmark->name)); bookmark_idx++; if (bookmark_idx < vec_length(doc->bookmarks)) next_bookmark = vec_get(doc->bookmarks, bookmark_idx); else next_bookmark = NULL; } for (; pos.x < cx; pos.x++) { char c = cell->c; byte a = cell->a & 0x0F; if (next_link) { if (doc_pos_compare(next_link->location.start, pos) == 0) { string_ptr s; int pos = string_last_chr(next_link->file, '.'); if (pos >= 0) { s = string_copy_sn(string_buffer(next_link->file), pos + 1); string_append_s(s, "html"); } else s = string_copy(next_link->file); fprintf(fp, "<a href=\"%s", string_buffer(s)); if (next_link->topic) fprintf(fp, "#%s", string_buffer(next_link->topic)); fprintf(fp, "\">"); string_free(s); } if (doc_pos_compare(next_link->location.stop, pos) == 0) { fprintf(fp, "</a>"); link_idx++; if (link_idx < vec_length(links)) next_link = vec_get(links, link_idx); else next_link = NULL; } } if (!c) break; if (a != old_a && c != ' ') { if (old_a != _INVALID_COLOR) fprintf(fp, "</font>"); fprintf(fp, "<font color=\"#%02x%02x%02x\">", angband_color_table[a][1], angband_color_table[a][2], angband_color_table[a][3] ); old_a = a; } switch (c) { case '&': fprintf(fp, "&"); break; case '<': fprintf(fp, "<"); break; case '>': fprintf(fp, ">"); break; default: fprintf(fp, "%c", c); break; } cell++; } fputc('\n', fp); } fprintf(fp, "</font>"); fprintf(fp, "</pre></body></html>\n"); vec_free(links); }