From 599617843e99807ef118ec57fdf9838e7920ab1d Mon Sep 17 00:00:00 2001 From: david parsons Date: Tue, 22 Mar 2022 17:01:14 -0700 Subject: [PATCH] implement a toc label uniquifier --- generate.c | 9 ++++----- markdown.c | 5 +++-- markdown.h | 7 +++++++ resource.c | 2 ++ tests/toc.t | 23 +++++++++++++++++++++++ toc.c | 37 +++++++++++++++++++++++++++++++++++-- 6 files changed, 74 insertions(+), 9 deletions(-) diff --git a/generate.c b/generate.c index cca1911..1505e7f 100644 --- a/generate.c +++ b/generate.c @@ -183,10 +183,9 @@ Qprintf(MMIOT *f, char *fmt, ...) /* Qanchor() prints out a suitable-for-id-tag version of a string */ static void -Qanchor(struct line *p, MMIOT *f) +Qanchor(char *name, MMIOT *f) { - mkd_string_to_anchor(T(p->text), S(p->text), - (mkd_sta_function_t)Qchar, f, 1, f); + mkd_string_to_anchor(name, strlen(name), (mkd_sta_function_t)Qchar, f, 1, f); } @@ -1528,14 +1527,14 @@ printheader(Paragraph *pp, MMIOT *f) Qprintf(f, "hnumber); if ( is_flag_set(&f->flags, MKD_TOC) ) { Qstring(" id=\"", f); - Qanchor(pp->text, f); + Qanchor(pp->label, f); Qchar('"', f); } Qchar('>', f); } else { if ( is_flag_set(&f->flags, MKD_TOC) ) { Qstring("text, f); + Qanchor(pp->label, f); Qstring("\">\n", f); } Qprintf(f, "", pp->hnumber); diff --git a/markdown.c b/markdown.c index 2380869..b0df270 100644 --- a/markdown.c +++ b/markdown.c @@ -20,8 +20,6 @@ typedef int (*stfu)(const void*,const void*); -typedef ANCHOR(Paragraph) ParagraphRoot; - static Paragraph *Pp(ParagraphRoot *, Line *, int); static Paragraph *compile(Line *, int, MMIOT *); @@ -1358,6 +1356,9 @@ compile(Line *ptr, int toplevel, MMIOT *f) else if ( ishdr(ptr, &hdr_type, &(f->flags) ) ) { p = Pp(&d, ptr, HDR); ptr = headerblock(p, hdr_type); + if ( is_flag_set(&(f->flags), MKD_TOC) ) + p->label = ___mkd_uniquetag(&d, T(p->text->text), + S(p->text->text)); } else { /* either markup or an html block element diff --git a/markdown.h b/markdown.h index 4efc40c..879444d 100644 --- a/markdown.h +++ b/markdown.h @@ -96,6 +96,7 @@ typedef struct paragraph { struct paragraph *next; /* next paragraph */ struct paragraph *down; /* recompiled contents of this paragraph */ struct line *text; /* all the text in this paragraph */ + char *label; /* toc label, uniqued */ char *ident; /* %id% tag for QUOTE */ char *lang; /* lang attribute for CODE */ enum { WHITESPACE=0, CODE, QUOTE, MARKUP, @@ -108,6 +109,8 @@ typedef struct paragraph { #define IS_CHECKED 0x02 } Paragraph; +typedef ANCHOR(Paragraph) ParagraphRoot; + enum { ETX, SETEXT }; /* header types */ /* reference-style links (and images) are stored in an array @@ -288,6 +291,10 @@ extern void __mkd_trim_line(Line *, int); extern int __mkd_io_strget(struct string_stream *); +/* toc uniquifier + */ +extern char *___mkd_uniquetag(ParagraphRoot *, char *, int); + /* utility function to do some operation and exit the current function * if it fails */ diff --git a/resource.c b/resource.c index 0b2dd06..256efe4 100644 --- a/resource.c +++ b/resource.c @@ -49,6 +49,8 @@ ___mkd_freeParagraph(Paragraph *p) ___mkd_freeParagraph(p->down); if (p->text) ___mkd_freeLines(p->text); + if (p->label) + free(p->label); if (p->ident) free(p->ident); if (p->lang) diff --git a/tests/toc.t b/tests/toc.t index 21283a7..7c15986 100644 --- a/tests/toc.t +++ b/tests/toc.t @@ -91,5 +91,28 @@ try '-T -ftoc,html5anchor' 'html5 multibyte chars' \

It’s an apostrophe

' +summary $0 + + +# Check that the uniquifier works +# + +title "uniquifying duplicate headers" + + +try '-T -ftoc' 'uniquifying duplicate labels' \ +'# this +# this' \ +' + +

this

+ + +

this

' + + summary $0 exit $rc diff --git a/toc.c b/toc.c index f8e7a15..3822573 100644 --- a/toc.c +++ b/toc.c @@ -72,8 +72,7 @@ mkd_toc(Document *p, char **doc) ++last_hnumber; } Csprintf(&res, "%*s
  • hnumber, ""); - mkd_string_to_anchor(T(srcp->text->text), - S(srcp->text->text), + mkd_string_to_anchor(srcp->label, strlen(srcp->label), (mkd_sta_function_t)Csputc, &res,1,p->ctx); Csprintf(&res, "\">"); @@ -104,6 +103,40 @@ mkd_toc(Document *p, char **doc) } +char * +___mkd_uniquetag(ParagraphRoot *pr, char *name, int length) +{ + Paragraph *content; + char *label; + int suffix; + int seq = 0; + + if ( !(pr && name) ) return 0; + + + suffix = length; + + label = calloc(1, suffix+20); + + strcpy(label, name); + +restart: + for ( content = T(*pr); content; content = content->next ) { + + if ( content->text && content->typ == HDR ) { + + if ( content->label && strcmp(label, content->label) == 0 ) { + /* collision; bump trailing sequence and try again + */ + sprintf(label+suffix, "_%d", ++seq); + goto restart; + } + } + } + return label; +} + + /* write an header index */ int