Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions internal/cbm/doclink_rst.c
Original file line number Diff line number Diff line change
Expand Up @@ -1606,18 +1606,19 @@ static const char *rst_body(rst_doc_t *d, int from, int to) {
return NULL;
}
int w = 0;
for (int k = from; k < to && w < RST_BODY_MAX; k++) {
bool full = false; /* the next character did not fit: the text ends before it */
for (int k = from; k < to && !full; k++) {
const char *s = d->L[k].s;
int len = d->L[k].len;
int i = 0;
while (i < len && w < RST_BODY_MAX) {
while (i < len && !full) {
int n;
uint32_t cp = rst_cp(s, len, i, &n);
if (rst_space_cp(cp)) {
i += n;
continue;
}
if (w > 0) {
if (w > 0 && w < RST_BODY_MAX) {
out[w++] = ' ';
}
while (i < len) {
Expand All @@ -1626,7 +1627,7 @@ static const char *rst_body(rst_doc_t *d, int from, int to) {
break;
}
if (w + n > RST_BODY_MAX) {
w = RST_BODY_MAX + 1; /* stop: the next character does not fit */
full = true; /* only whole characters are written: none is cut */
break;
}
memcpy(out + w, s + i, (size_t)n);
Expand All @@ -1635,12 +1636,6 @@ static const char *rst_body(rst_doc_t *d, int from, int to) {
}
}
}
if (w > RST_BODY_MAX) {
w = RST_BODY_MAX;
while (w > 0 && ((unsigned char)out[w] & 0xC0) == 0x80) {
w--;
}
}
while (w > 0 && out[w - 1] == ' ') {
w--;
}
Expand Down
39 changes: 39 additions & 0 deletions tests/test_doc_links_rst.c
Original file line number Diff line number Diff line change
Expand Up @@ -558,9 +558,48 @@ TEST(rst_ship_gate) {
PASS();
}

/* A section's text is cut before the first character that does not fit its
* 500 bytes: only whole characters are written, and the text ends at the last
* one written (it once tested an unwritten byte past the end, so where the
* text ended depended on what that memory held). */
TEST(rst_section_text_cut) {
static const struct {
const char *tail;
const char *want_tail;
} cases[] = {
{" xxxxx tail", " xxxxx"}, /* a word that ends at byte 500 */
{" xxxx\xc3\xa9 more", " xxxx"}, /* a two-byte character that would cross it */
{" yyyyyyyy", " yyyyy"}, /* a word cut at a character */
};
for (size_t c = 0; c < sizeof(cases) / sizeof(cases[0]); c++) {
char doc[1024];
char want[600];
int n = snprintf(doc, sizeof(doc), "Title\n=====\n\n");
int w = 0;
for (int k = 0; k < 99; k++) { /* 99 words: 494 bytes, nine per paragraph */
n += snprintf(doc + n, sizeof(doc) - (size_t)n, "%sabcd",
k == 0 ? ""
: k % 9 == 0 ? "\n\n"
: " ");
w += snprintf(want + w, sizeof(want) - (size_t)w, "%sabcd", k ? " " : "");
}
snprintf(doc + n, sizeof(doc) - (size_t)n, "%s\n", cases[c].tail);
snprintf(want + w, sizeof(want) - (size_t)w, "%s", cases[c].want_tail);
CBMFileResult *r = dm_extract(doc, CBM_LANG_RST, "docs/cut.rst");
ASSERT_NOT_NULL(r);
const CBMDefinition *t = rst_def(r, "Section", "Title");
ASSERT_NOT_NULL(t);
ASSERT_NOT_NULL(t->docstring);
ASSERT_STR_EQ(t->docstring, want);
cbm_free_result(r);
}
PASS();
}

SUITE(doc_links_rst) {
dm_ship_held_families();
RUN_TEST(rst_scan_structure);
RUN_TEST(rst_section_text_cut);
RUN_TEST(rst_python_scope_blob);
RUN_TEST(rst_links_pipeline);
RUN_TEST(rst_c_member_owner);
Expand Down
Loading