Add bxstring support to 'generate' module

This commit is contained in:
Thomas Jensen 2023-05-14 22:17:10 +02:00
parent 232be1cdc4
commit 6cea61c327
No known key found for this signature in database
GPG Key ID: A4ACEE270D0FB7DB
12 changed files with 419 additions and 157 deletions

View File

@ -19,8 +19,10 @@
#include "config.h"
#include <stdarg.h>
#include <string.h>
#include <unictype.h>
#include <unistdio.h>
#include <unistr.h>
#include <uniwidth.h>
@ -271,6 +273,42 @@ bxstr_t *bxs_strcat(bxstr_t *pString, uint32_t *pToAppend)
bxstr_t *bxs_concat(size_t count, ...)
{
if (count < 1) {
return bxs_from_ascii("");
}
size_t total_len = 0;
uint32_t *src;
va_list va;
va_start(va, count);
for (size_t i = 0; i < count; i++) {
src = va_arg(va, uint32_t *);
if (src != NULL) {
total_len += u32_strlen(src);
} else {
total_len += 6; /* strlen("(NULL)") == 6 */
}
}
va_end(va);
uint32_t *utf32 = (uint32_t *) malloc((total_len + 1) * sizeof(uint32_t));
char *format = repeat("%llU", count); /* "%llU" stands for a UTF-32 string */
va_start(va, count);
u32_vsnprintf(utf32, total_len + 1, format, va);
va_end(va);
bxstr_t *result = bxs_from_unicode(utf32);
BFREE(format);
BFREE(utf32);
return result;
}
uint32_t *bxs_strchr(bxstr_t *pString, ucs4_t c, int *cursor)
{
uint32_t *result = NULL;

View File

@ -111,6 +111,16 @@ bxstr_t *bxs_trimdup(bxstr_t *pString, size_t start_idx, size_t end_idx);
bxstr_t *bxs_strcat(bxstr_t *pString, uint32_t *pToAppend);
/**
* Concatenate all given strings into a new string.
* @param count number of strings given in the following
* @param <...> the strings, all of them `uint32_t *`. NULL is discouraged as an argument, but will probably be
* printed as "(NULL)". (That's what libunistring did in our tests, but they promise nothing in their docs.)
* @return a new string, for which new memory was allocated, might be empty, but never NULL
*/
bxstr_t *bxs_concat(size_t count, ...);
/**
* Return a pointer to the first visible occurrence of the character `c` in the string `pString`.
* Invisible characters are ignored.

View File

@ -408,10 +408,55 @@ static int vert_precalc(const sentry_t *sarr,
/**
* Calculate the maximum number of characters in a line of a horizontal side (top or bottom). This excludes corners,
* which always belong to the vertical sides. This is needed for allocating space for the assembled sides, which will
* include shapes multiple times, and also include invisible characters.
* @param sarr all shapes of the current design
* @param side the side to calculate (`north_side` or `south_side`)
* @param iltf the numbers of times that a shape shall appear (array of *three* values)
* @param target_width the number of columns we must reach
* @param target_height the number of lines of the side
* @return the number of characters(!), visible plus invisible, that suffice to store every line of that side
*/
static size_t horiz_chars_required(const sentry_t *sarr, const shape_t *side, size_t *iltf, size_t target_width,
size_t target_height)
{
size_t *lens = (size_t *) calloc(target_height, sizeof(size_t));
size_t *iltf_copy = (size_t *) malloc(3 * sizeof(size_t));
memcpy(iltf_copy, iltf, 3 * sizeof(size_t));
int cshape = (side == north_side) ? 0 : 2;
for (size_t j = 0; j < target_width; j += sarr[side[cshape + 1]].width) {
while (iltf_copy[cshape] == 0) {
cshape += (side == north_side) ? 1 : -1;
}
for (size_t line = 0; line < target_height; ++line) {
lens[line] += sarr[side[cshape + 1]].mbcs[line]->num_chars;
}
iltf_copy[cshape] -= sarr[side[cshape + 1]].width;
}
size_t result = 0;
for (size_t i = 0; i < target_height; i++) {
if (lens[i] > result) {
result = lens[i];
}
}
BFREE(lens);
BFREE(iltf_copy);
#ifdef DEBUG
fprintf (stderr, "%s side required characters: %d\n", (side == north_side) ? "Top": "Bottom", (int) result);
#endif
return result;
}
static int vert_assemble(const sentry_t *sarr, const shape_t *seite,
size_t *iltf, sentry_t *result)
/*
*
* RETURNS: == 0 on success (result values are set)
* != 0 on error
*
@ -422,17 +467,23 @@ static int vert_assemble(const sentry_t *sarr, const shape_t *seite,
size_t line;
int cshape; /* current shape (idx to iltf) */
size_t max_chars = horiz_chars_required(sarr, seite, iltf, result->width, result->height);
uint32_t **mbcs_tmp = (uint32_t **) calloc(result->height, sizeof(uint32_t *));
for (line = 0; line < result->height; ++line) {
result->chars[line] = (char *) calloc(1, result->width + 1);
if (result->chars[line] == NULL) {
perror(PROJECT);
if ((long) --line >= 0) {
do {
BFREE (mbcs_tmp[line]);
BFREE (result->chars[line--]);
} while ((long) line >= 0);
}
BFREE(mbcs_tmp);
return 1; /* out of memory */
}
mbcs_tmp[line] = (uint32_t *) calloc(max_chars + 1, sizeof(uint32_t));
}
cshape = (seite == north_side) ? 0 : 2;
@ -443,10 +494,16 @@ static int vert_assemble(const sentry_t *sarr, const shape_t *seite,
}
for (line = 0; line < result->height; ++line) {
strcat(result->chars[line], sarr[seite[cshape + 1]].chars[line]);
u32_strcat(mbcs_tmp[line], sarr[seite[cshape + 1]].mbcs[line]->memory);
}
iltf[cshape] -= sarr[seite[cshape + 1]].width;
}
for (line = 0; line < result->height; ++line) {
result->mbcs[line] = bxs_from_unicode(mbcs_tmp[line]);
}
BFREE(mbcs_tmp);
return 0; /* all clear */
}
@ -473,10 +530,11 @@ static void horiz_assemble(const sentry_t *sarr, const shape_t *seite,
for (j = 0; j < sarr[ctop].height; ++j) {
result->chars[j] = sarr[ctop].chars[j];
result->mbcs[j] = sarr[ctop].mbcs[j];
}
for (j = 0; j < sarr[cbottom].height; ++j) {
result->chars[result->height - sarr[cbottom].height + j] =
sarr[cbottom].chars[j];
result->chars[result->height - sarr[cbottom].height + j] = sarr[cbottom].chars[j];
result->mbcs[result->height - sarr[cbottom].height + j] = sarr[cbottom].mbcs[j];
}
sc = 0;
@ -493,6 +551,7 @@ static void horiz_assemble(const sentry_t *sarr, const shape_t *seite,
sc = 0;
}
result->chars[j] = sarr[seite[cshape + 1]].chars[sc];
result->mbcs[j] = sarr[seite[cshape + 1]].mbcs[sc];
++sc;
iltf[cshape] -= 1;
}
@ -525,23 +584,25 @@ static int horiz_generate(sentry_t *tresult, sentry_t *bresult)
}
bresult->width = tresult->width;
#ifdef DEBUG
fprintf (stderr, "Top side box rect width %d, height %d.\n",
(int) tresult->width, (int) tresult->height);
fprintf (stderr, "Top columns to fill: %s %d, %s %d, %s %d.\n",
shape_name[north_side[1]], (int) tiltf[0],
shape_name[north_side[2]], (int) tiltf[1],
shape_name[north_side[3]], (int) tiltf[2]);
fprintf (stderr, "Bottom side box rect width %d, height %d.\n",
(int) bresult->width, (int) bresult->height);
fprintf (stderr, "Bottom columns to fill: %s %d, %s %d, %s %d.\n",
shape_name[south_side[1]], (int) biltf[0],
shape_name[south_side[2]], (int) biltf[1],
shape_name[south_side[3]], (int) biltf[2]);
#endif
#ifdef DEBUG
fprintf (stderr, "Top side box rect width %d, height %d.\n",
(int) tresult->width, (int) tresult->height);
fprintf (stderr, "Top columns to fill: %s %d, %s %d, %s %d.\n",
shape_name[north_side[1]], (int) tiltf[0],
shape_name[north_side[2]], (int) tiltf[1],
shape_name[north_side[3]], (int) tiltf[2]);
fprintf (stderr, "Bottom side box rect width %d, height %d.\n",
(int) bresult->width, (int) bresult->height);
fprintf (stderr, "Bottom columns to fill: %s %d, %s %d, %s %d.\n",
shape_name[south_side[1]], (int) biltf[0],
shape_name[south_side[2]], (int) biltf[1],
shape_name[south_side[3]], (int) biltf[2]);
#endif
tresult->chars = (char **) calloc(tresult->height, sizeof(char *));
tresult->mbcs = (bxstr_t **) calloc(tresult->height, sizeof(bxstr_t *));
bresult->chars = (char **) calloc(bresult->height, sizeof(char *));
bresult->mbcs = (bxstr_t **) calloc(bresult->height, sizeof(bxstr_t *));
if (tresult->chars == NULL || bresult->chars == NULL) {
return 1;
}
@ -555,7 +616,7 @@ static int horiz_generate(sentry_t *tresult, sentry_t *bresult)
return rc;
}
#ifdef DEBUG
#ifdef DEBUG
{
/*
* Debugging code - Output horizontal sides of box
@ -563,16 +624,16 @@ static int horiz_generate(sentry_t *tresult, sentry_t *bresult)
size_t j;
fprintf(stderr, "TOP SIDE:\n");
for (j = 0; j < tresult->height; ++j) {
fprintf(stderr, " %2d: \'%s\'\n", (int) j,
tresult->chars[j] ? tresult->chars[j] : "(null)");
fprintf(stderr, " %2d: \'%s\' - \'%s\'\n", (int) j,
bxs_to_output(tresult->mbcs[j]), tresult->chars[j]);
}
fprintf(stderr, "BOTTOM SIDE:\n");
for (j = 0; j < bresult->height; ++j) {
fprintf(stderr, " %2d: \'%s\'\n", (int) j,
bresult->chars[j] ? bresult->chars[j] : "(null)");
fprintf(stderr, " %2d: \'%s\' - '%s'\n", (int) j,
bxs_to_output(bresult->mbcs[j]), bresult->chars[j]);
}
}
#endif
#endif
return 0; /* all clear */
}
@ -609,51 +670,59 @@ static int vert_generate(sentry_t *lresult, sentry_t *rresult)
rresult->height = vspace +
opt.design->shape[NE].height + opt.design->shape[SE].height;
#ifdef DEBUG
fprintf(stderr, "Left side box rect width %d, height %d, vspace %d.\n",
(int) lresult->width, (int) lresult->height, (int) vspace);
fprintf(stderr, "Left lines to fill: %s %d, %s %d, %s %d.\n",
shape_name[west_side[1]], (int) leftiltf[0],
shape_name[west_side[2]], (int) leftiltf[1],
shape_name[west_side[3]], (int) leftiltf[2]);
fprintf(stderr, "Right side box rect width %d, height %d, vspace %d.\n",
(int) rresult->width, (int) rresult->height, (int) vspace);
fprintf(stderr, "Right lines to fill: %s %d, %s %d, %s %d.\n",
shape_name[east_side[1]], (int) rightiltf[0],
shape_name[east_side[2]], (int) rightiltf[1],
shape_name[east_side[3]], (int) rightiltf[2]);
#endif
#ifdef DEBUG
fprintf(stderr, "Left side box rect width %d, height %d, vspace %d.\n",
(int) lresult->width, (int) lresult->height, (int) vspace);
fprintf(stderr, "Left lines to fill: %s %d, %s %d, %s %d.\n",
shape_name[west_side[1]], (int) leftiltf[0],
shape_name[west_side[2]], (int) leftiltf[1],
shape_name[west_side[3]], (int) leftiltf[2]);
fprintf(stderr, "Right side box rect width %d, height %d, vspace %d.\n",
(int) rresult->width, (int) rresult->height, (int) vspace);
fprintf(stderr, "Right lines to fill: %s %d, %s %d, %s %d.\n",
shape_name[east_side[1]], (int) rightiltf[0],
shape_name[east_side[2]], (int) rightiltf[1],
shape_name[east_side[3]], (int) rightiltf[2]);
#endif
lresult->chars = (char **) calloc(lresult->height, sizeof(char *));
if (lresult->chars == NULL) {
return 1;
}
lresult->mbcs = (bxstr_t **) calloc(lresult->height, sizeof(bxstr_t *));
if (lresult->mbcs == NULL) {
return 1;
}
rresult->chars = (char **) calloc(rresult->height, sizeof(char *));
if (rresult->chars == NULL) {
return 1;
}
rresult->mbcs = (bxstr_t **) calloc(rresult->height, sizeof(bxstr_t *));
if (rresult->mbcs == NULL) {
return 1;
}
horiz_assemble(opt.design->shape, west_side, leftiltf, lresult);
horiz_assemble(opt.design->shape, east_side, rightiltf, rresult);
#if defined(DEBUG) && 1
{
/*
* Debugging code - Output left and right side of box
*/
size_t j;
fprintf(stderr, "LEFT SIDE:\n");
for (j = 0; j < lresult->height; ++j) {
fprintf(stderr, " %2d: \'%s\'\n", (int) j,
lresult->chars[j] ? lresult->chars[j] : "(null)");
#if defined(DEBUG) && 1
{
/*
* Debugging code - Output left and right side of box
*/
size_t j;
fprintf(stderr, "LEFT SIDE:\n");
for (j = 0; j < lresult->height; ++j) {
fprintf(stderr, " %2d: \'%s\' - \'%s\'\n", (int) j,
bxs_to_output(lresult->mbcs[j]), lresult->chars[j]);
}
fprintf(stderr, "RIGHT SIDE:\n");
for (j = 0; j < rresult->height; ++j) {
fprintf(stderr, " %2d: \'%s\' - \'%s\'\n", (int) j,
bxs_to_output(rresult->mbcs[j]), rresult->chars[j]);
}
}
fprintf(stderr, "RIGHT SIDE:\n");
for (j = 0; j < rresult->height; ++j) {
fprintf(stderr, " %2d: \'%s\'\n", (int) j,
rresult->chars[j] ? rresult->chars[j] : "(null)");
}
}
#endif
#endif
return 0; /* all clear */
}
@ -793,31 +862,27 @@ int output_box(const sentry_t *thebox)
{
size_t j;
size_t nol = thebox[BRIG].height; /* number of output lines */
char *indentspc;
int indentspclen;
size_t vfill, vfill1, vfill2; /* empty lines/columns in box */
size_t hfill;
char *hfill1, *hfill2; /* space before/after text */
uint32_t *hfill1, *hfill2; /* space before/after text */
size_t hpl, hpr;
char obuf[LINE_MAX_BYTES + 1]; /* final output buffer */
size_t obuf_len; /* length of content of obuf */
size_t skip_start; /* lines to skip for box top */
size_t skip_end; /* lines to skip for box bottom */
size_t skip_left; /* true if left box part is to be skipped */
int ntabs, nspcs; /* needed for unexpand of tabs */
char *restored_indent;
size_t ntabs, nspcs; /* needed for unexpand of tabs */
#ifdef DEBUG
fprintf (stderr, "Padding used: left %d, top %d, right %d, bottom %d\n",
opt.design->padding[BLEF], opt.design->padding[BTOP],
opt.design->padding[BRIG], opt.design->padding[BBOT]);
#endif
#ifdef DEBUG
fprintf (stderr, "Padding used: left %d, top %d, right %d, bottom %d\n",
opt.design->padding[BLEF], opt.design->padding[BTOP],
opt.design->padding[BRIG], opt.design->padding[BBOT]);
#endif
/*
* Create string of spaces for indentation
*/
indentspc = NULL;
ntabs = nspcs = indentspclen = 0;
uint32_t *indentspc = NULL;
size_t indentspclen = 0;
ntabs = nspcs = 0;
if (opt.design->indentmode == 'b') {
if (opt.tabexp == 'u') {
ntabs = input.indent / opt.tabstop;
@ -827,23 +892,24 @@ int output_box(const sentry_t *thebox)
else {
indentspclen = input.indent;
}
indentspc = (char *) malloc(indentspclen + 1);
indentspc = (uint32_t *) malloc((indentspclen + 1) * sizeof(uint32_t));
if (indentspc == NULL) {
perror(PROJECT);
return 1;
}
if (opt.tabexp == 'u') {
memset(indentspc, (int) '\t', ntabs);
memset(indentspc + ntabs, (int) ' ', nspcs);
u32_set(indentspc, char_tab, ntabs);
u32_set(indentspc + ntabs, char_space, nspcs);
}
else {
memset(indentspc, (int) ' ', indentspclen);
u32_set(indentspc, char_space, indentspclen);
}
indentspc[indentspclen] = '\0';
set_char_at(indentspc, indentspclen, char_nul);
}
else {
indentspc = (char *) strdup("");
indentspc = new_empty_string32();
if (indentspc == NULL) {
perror(PROJECT);
return 1;
@ -875,16 +941,16 @@ int output_box(const sentry_t *thebox)
* Provide strings for horizontal text alignment.
*/
hfill = thebox[BTOP].width - input.maxline;
hfill1 = (char *) malloc(hfill + 1);
hfill2 = (char *) malloc(hfill + 1);
hfill1 = (uint32_t *) malloc((hfill + 1) * sizeof(uint32_t));
hfill2 = (uint32_t *) malloc((hfill + 1) * sizeof(uint32_t));
if (!hfill1 || !hfill2) {
perror(PROJECT);
return 1;
}
memset(hfill1, (int) ' ', hfill + 1);
memset(hfill2, (int) ' ', hfill + 1);
hfill1[hfill] = '\0';
hfill2[hfill] = '\0';
u32_set(hfill1, char_space, hfill);
u32_set(hfill2, char_space, hfill);
set_char_at(hfill1, hfill, char_nul);
set_char_at(hfill2, hfill, char_nul);
hpl = 0;
hpr = 0;
if (hfill == 1) {
@ -914,15 +980,15 @@ int output_box(const sentry_t *thebox)
}
hfill += opt.design->padding[BLEF] + opt.design->padding[BRIG];
}
hfill1[hpl] = '\0';
hfill2[hpr] = '\0';
set_char_at(hfill1, hpl, char_nul);
set_char_at(hfill2, hpr, char_nul);
#if defined(DEBUG)
fprintf(stderr, "Alignment: hfill %d hpl %d hpr %d, vfill %d vfill1 %d vfill2 %d.\n",
(int) hfill, (int) hpl, (int) hpr, (int) vfill, (int) vfill1, (int) vfill2);
fprintf(stderr, " hfill1 = \"%s\"; hfill2 = \"%s\"; indentspc = \"%s\";\n",
hfill1, hfill2, indentspc);
#endif
#if defined(DEBUG)
fprintf(stderr, "Alignment: hfill %d hpl %d hpr %d, vfill %d vfill1 %d vfill2 %d.\n",
(int) hfill, (int) hpl, (int) hpr, (int) vfill, (int) vfill1, (int) vfill2);
fprintf(stderr, " hfill1 = \"%s\"; hfill2 = \"%s\"; indentspc = \"%s\";\n",
u32_strconv_to_output(hfill1), u32_strconv_to_output(hfill2), u32_strconv_to_output(indentspc));
#endif
/*
* Find out if and how many leading or trailing blank lines must be
@ -948,21 +1014,28 @@ int output_box(const sentry_t *thebox)
/*
* Generate actual output
*/
bxstr_t *obuf = NULL; /* final output string */
uint32_t *restored_indent;
uint32_t *empty_string = new_empty_string32();
for (j = skip_start; j < nol - skip_end; ++j) {
if (j < thebox[BTOP].height) { /* box top */
restored_indent = tabbify_indent(0, indentspc, indentspclen);
concat_strings(obuf, LINE_MAX_BYTES + 1, 4, restored_indent,
skip_left ? "" : thebox[BLEF].chars[j], thebox[BTOP].chars[j],
thebox[BRIG].chars[j]);
obuf = bxs_concat(4, restored_indent,
skip_left ? empty_string : thebox[BLEF].mbcs[j]->memory,
thebox[BTOP].mbcs[j]->memory,
thebox[BRIG].mbcs[j]->memory);
}
else if (vfill1) { /* top vfill */
restored_indent = tabbify_indent(0, indentspc, indentspclen);
concat_strings(obuf, LINE_MAX_BYTES + 1, 4, restored_indent,
skip_left ? "" : thebox[BLEF].chars[j], nspaces(thebox[BTOP].width),
thebox[BRIG].chars[j]);
uint32_t *wspc = u32_nspaces(thebox[BTOP].width);
obuf = bxs_concat(4, restored_indent,
skip_left ? empty_string : thebox[BLEF].mbcs[j]->memory,
wspc,
thebox[BRIG].mbcs[j]->memory);
--vfill1;
BFREE(wspc);
}
else if (j < nol - thebox[BBOT].height) {
@ -971,46 +1044,52 @@ int output_box(const sentry_t *thebox)
int shift = justify_line(input.lines + ti, hpr - hpl);
restored_indent = tabbify_indent(ti, indentspc, indentspclen);
uint32_t *mbtext_shifted = advance32(input.lines[ti].mbtext, shift < 0 ? (size_t) (-shift) : 0);
concat_strings(obuf, LINE_MAX_BYTES + 1, 8, restored_indent,
skip_left ? "" : thebox[BLEF].chars[j], hfill1,
ti >= 0 && shift > 0 ? nspaces(shift) : "",
ti >= 0 ? u32_strconv_to_output(mbtext_shifted) : "",
hfill2, nspaces(input.maxline - input.lines[ti].len - shift),
thebox[BRIG].chars[j]);
uint32_t *spc1 = empty_string;
if (ti >= 0 && shift > 0) {
spc1 = u32_nspaces(shift);
}
uint32_t *spc2 = u32_nspaces(input.maxline - input.lines[ti].len - shift);
obuf = bxs_concat(8, restored_indent,
skip_left ? empty_string : thebox[BLEF].mbcs[j]->memory, hfill1, spc1,
ti >= 0 ? mbtext_shifted : empty_string, hfill2, spc2,
thebox[BRIG].mbcs[j]->memory);
if (spc1 != empty_string) {
BFREE(spc1);
}
BFREE(spc2);
}
else { /* bottom vfill */
restored_indent = tabbify_indent(input.num_lines - 1, indentspc, indentspclen);
concat_strings(obuf, LINE_MAX_BYTES + 1, 4, restored_indent,
skip_left ? "" : thebox[BLEF].chars[j], nspaces(thebox[BTOP].width),
thebox[BRIG].chars[j]);
uint32_t *spc = u32_nspaces(thebox[BTOP].width);
obuf = bxs_concat(4, restored_indent,
skip_left ? empty_string : thebox[BLEF].mbcs[j]->memory,
spc,
thebox[BRIG].mbcs[j]->memory);
BFREE(spc);
}
}
else { /* box bottom */
restored_indent = tabbify_indent(input.num_lines - 1, indentspc, indentspclen);
concat_strings(obuf, LINE_MAX_BYTES + 1, 4, restored_indent,
skip_left ? "" : thebox[BLEF].chars[j],
thebox[BBOT].chars[j - (nol - thebox[BBOT].height)],
thebox[BRIG].chars[j]);
obuf = bxs_concat(4, restored_indent,
skip_left ? empty_string : thebox[BLEF].mbcs[j]->memory,
thebox[BBOT].mbcs[j - (nol - thebox[BBOT].height)]->memory,
thebox[BRIG].mbcs[j]->memory);
}
obuf_len = strlen(obuf);
bxstr_t *obuf_trimmed = bxs_rtrim(obuf);
fprintf(opt.outfile, "%s%s", bxs_to_output(obuf_trimmed),
(input.final_newline || j < nol - skip_end - 1 ? opt.eol : ""));
if (obuf_len > LINE_MAX_BYTES) {
size_t newlen = LINE_MAX_BYTES;
btrim(obuf, &newlen);
}
else {
btrim(obuf, &obuf_len);
}
bxs_free(obuf);
bxs_free(obuf_trimmed);
if (opt.tabexp == 'k') {
BFREE (restored_indent);
BFREE(restored_indent);
}
fprintf(opt.outfile, "%s%s", obuf, (input.final_newline || j < nol - skip_end - 1 ? opt.eol : ""));
}
BFREE (indentspc);
BFREE (empty_string);
BFREE (hfill1);
BFREE (hfill2);
return 0; /* all clear */
@ -1018,4 +1097,4 @@ int output_box(const sentry_t *thebox)
/*EOF*/ /* vim: set sw=4: */
/* vim: set cindent sw=4: */

View File

@ -242,6 +242,27 @@ char *concat_strings_alloc(size_t count, ...)
char *repeat(char *s, size_t count)
{
if (s == NULL) {
return NULL;
}
size_t len = strlen(s);
char *result = (char *) malloc(count * len + 1);
if (result != NULL) {
char *dest = result;
for (size_t i = 0; i < count; i++) {
strcpy(dest, s);
dest += len;
}
*dest = '\0';
}
return result;
}
int empty_line(const line_t *line)
/*
* Return true if line is empty.
@ -484,26 +505,10 @@ size_t my_strrspn(const char *s, const char *accept)
char *tabbify_indent(const size_t lineno, char *indentspc, const size_t indentspc_len)
/*
* Checks if tab expansion mode is "keep", and if so, calculates a new
* indentation string based on the one given. The new string contains
* tabs in their original positions.
*
* lineno index of the input line we are referring to
* indentspc previously calculated "space-only" indentation string
* (may be NULL). This is only used when opt.tabexp != 'k',
* in which case it will be used as the function result.
* indentspc_len desired result length, measured in spaces only
*
* RETURNS: if successful and opt.tabexp == 'k': new string
* on error (invalid input or out of memory): NULL
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
uint32_t *tabbify_indent(const size_t lineno, uint32_t *indentspc, const size_t indentspc_len)
{
size_t i;
char *result;
uint32_t *result;
size_t result_len;
if (opt.tabexp != 'k') {
@ -513,16 +518,16 @@ char *tabbify_indent(const size_t lineno, char *indentspc, const size_t indentsp
return NULL;
}
if (indentspc_len == 0) {
return (char *) strdup("");
return new_empty_string32();
}
result = (char *) malloc(indentspc_len + 1);
result = (uint32_t *) malloc((indentspc_len + 1) * sizeof(uint32_t));
if (result == NULL) {
perror(PROJECT);
return NULL;
}
memset(result, (int) ' ', indentspc_len);
result[indentspc_len] = '\0';
u32_set(result, char_space, indentspc_len);
set_char_at(result, indentspc_len, char_nul);
result_len = indentspc_len;
for (i = 0; i < input.lines[lineno].tabpos_len && input.lines[lineno].tabpos[i] < indentspc_len; ++i) {
@ -531,9 +536,9 @@ char *tabbify_indent(const size_t lineno, char *indentspc, const size_t indentsp
if (tpos + nspc > input.indent) {
break;
}
result[tpos] = '\t';
set_char_at(result, tpos, char_tab);
result_len -= nspc - 1;
result[result_len] = '\0';
set_char_at(result, result_len, char_nul);
}
return result;

View File

@ -59,8 +59,8 @@ char *my_strnrstr(const char *s1, const char *s2, const size_t s2_len, int skip)
/**
* Calculates the length (in bytes) of the segment at the end of `s` which consists entirely of bytes in `accept`.
* This is like `strspn()`, but from the end of the string.
* @param <s> the string to search
* @param <accept> acceptable characters that form the trailing segment
* @param s the string to search
* @param accept acceptable characters that form the trailing segment
* @return the number of bytes found as described above
*/
size_t my_strrspn(const char *s, const char *accept);
@ -75,9 +75,9 @@ int strisno(const char *s);
/**
* Concatenate variable number of strings into one. This would normally be achieved via snprintf(), but that's not
* available on all platforms where boxes is compiled.
* @param <count> number of strings given in the following
* @param count number of strings given in the following
* @param <...> the strings
* @returns a new string, for which new memory was allocated
* @return a new string, for which new memory was allocated
*/
char *concat_strings_alloc(size_t count, ...);
@ -85,7 +85,27 @@ char *concat_strings_alloc(size_t count, ...);
void concat_strings(char *dst, int max_len, int count, ...);
char *tabbify_indent(const size_t lineno, char *indentspc, const size_t indentspc_len);
/**
* Repeat the string `s` `count` times.
* @param s the string to repeat
* @param count the number of times the string should be repeated
* @return a new string, for which new memory was allocated, or NULL if `s` was NULL or out of memory
*/
char *repeat(char *s, size_t count);
/**
* Checks if tab expansion mode is "keep", and if so, calculates a new indentation string based on the one given.
* The new string contains tabs in their original positions.
*
* @param lineno index of the input line we are referring to
* @param indentspc previously calculated "space-only" indentation string (may be NULL). This is only used when
* opt.tabexp != 'k', in which case it will be used as the function result.
* @param indentspc_len desired result length, measured in spaces only
* @return if successful and opt.tabexp == 'k': new string;
* on error (invalid input or out of memory): NULL
*/
uint32_t *tabbify_indent(const size_t lineno, uint32_t *indentspc, const size_t indentspc_len);
char *nspaces(const size_t n);
@ -208,4 +228,4 @@ FILE *bx_fopen(char *pathname, char *mode);
#endif
/*EOF*/ /* vim: set cindent sw=4: */
/* vim: set cindent sw=4: */

View File

@ -318,4 +318,20 @@ char *to_utf8(uint32_t *src)
}
/*EOF*/ /* vim: set sw=4: */
uint32_t *u32_nspaces(const size_t n)
{
uint32_t *result = (uint32_t *) malloc((n + 1) * sizeof(uint32_t));
if (result == NULL) {
perror(PROJECT);
return NULL;
}
if (n > 0) {
u32_set(result, char_space, n);
}
set_char_at(result, n, char_nul);
return result;
}
/* vim: set cindent sw=4: */

View File

@ -146,6 +146,7 @@ uint32_t *advance_next32(const uint32_t *s, size_t *invis);
* Determine a new position in the given string s with the given offset of visible characters.
* If the character right in front of the target character is invisible, then the pointer is moved to the start of
* that invisible sequence. The purpose is to catch any escape sequences which would for example color the character.
* CHECK This is redundant, bxstrings can do this better.
*
* @param s The pointer to the start position. Is assumed to point either at the ESC at the start of an escape
* sequence, or to be positioned outside an escape sequence.
@ -218,6 +219,14 @@ const char *check_encoding(const char *manual_encoding, const char *system_encod
char *to_utf8(uint32_t *src);
/**
* Create a new string of `n` spaces.
* @param n number of spaces
* @return a new string of length `n`, for which new memory was allocated
*/
uint32_t *u32_nspaces(const size_t n);
#endif
/*EOF*/ /* vim: set cindent sw=4: */

View File

@ -317,10 +317,8 @@ void test_ansi_unicode_broken_escapes(void **state)
* "\x1b[" (broken, counts as invisible, and belonging to the previous 'b')
*/
uint32_t *ustr32 = u32_strconv_from_arg("\x1b[38;5;203 XX \x1b[0m__\x1b[38;5;203mb\x1b[", "ASCII");
fprintf(stderr, "BOO\n");
assert_non_null(ustr32);
bxstr_t *actual = bxs_from_unicode(ustr32);
fprintf(stderr, "BAA\n");
assert_non_null(actual);
assert_non_null(actual->memory);
@ -1059,4 +1057,63 @@ void test_bxs_free_null(void **state)
}
void test_bxs_concat(void **state)
{
UNUSED(state);
bxstr_t *actual = bxs_concat(0);
assert_non_null(actual);
assert_string_equal("", actual->ascii);
assert_int_equal(0, actual->num_chars);
uint32_t *s1 = u32_strconv_from_arg("\x1b[38;5;203mX\x1b[0m", "ASCII");
uint32_t *s2 = u32_strconv_from_arg("---", "ASCII");
uint32_t *s3 = u32_strconv_from_arg("ÄÖÜ\x1b[38;5;203m!\x1b[0m", "UTF-8");
uint32_t *s4 = u32_strconv_from_arg(" ", "ASCII");
actual = bxs_concat(5, s1, s2, s3, s4, s4);
assert_non_null(actual);
assert_string_equal("X---xxx! ", actual->ascii);
assert_int_equal(12, actual->num_chars_visible);
assert_int_equal(30, actual->num_chars_invisible);
assert_int_equal(42, actual->num_chars);
assert_int_equal(4, actual->trailing);
assert_int_equal(0, actual->indent);
assert_int_equal(12, actual->num_columns);
BFREE(s1);
BFREE(s2);
BFREE(s3);
BFREE(s4);
bxs_free(actual);
}
void test_bxs_concat_nullarg(void **state)
{
UNUSED(state);
uint32_t *s1 = u32_strconv_from_arg("\x1b[38;5;203mAB", "ASCII");
uint32_t *s2 = u32_strconv_from_arg("-CD-", "ASCII");
uint32_t *s3 = u32_strconv_from_arg("-EF\x1b[0m", "ASCII");
bxstr_t *actual = bxs_concat(4, s1, s2, NULL, s3);
assert_non_null(actual);
assert_string_equal("AB-CD-(NULL)-EF", actual->ascii);
assert_int_equal(15, actual->num_chars_visible);
assert_int_equal(15, actual->num_chars_invisible);
assert_int_equal(30, actual->num_chars);
assert_int_equal(0, actual->trailing);
assert_int_equal(0, actual->indent);
assert_int_equal(15, actual->num_columns);
BFREE(s1);
BFREE(s2);
BFREE(s3);
bxs_free(actual);
}
/* vim: set cindent sw=4: */

View File

@ -79,6 +79,9 @@ void test_bxs_valid_in_filename_error(void **state);
void test_bxs_free_null(void **state);
void test_bxs_concat(void **state);
void test_bxs_concat_nullarg(void **state);
#endif

View File

@ -105,7 +105,8 @@ int main(void)
cmocka_unit_test(test_is_ascii_id_valid),
cmocka_unit_test(test_is_ascii_id_invalid),
cmocka_unit_test(test_is_ascii_id_strict_valid),
cmocka_unit_test(test_is_ascii_id_strict_invalid)
cmocka_unit_test(test_is_ascii_id_strict_invalid),
cmocka_unit_test(test_repeat)
};
const struct CMUnitTest unicode_tests[] = {
@ -157,11 +158,12 @@ int main(void)
cmocka_unit_test_setup(test_bxs_is_visible_char, beforeTest),
cmocka_unit_test_setup(test_bxs_filter_visible, beforeTest),
cmocka_unit_test_setup(test_bxs_filter_visible_none, beforeTest),
cmocka_unit_test_setup(test_bxs_strcmp, beforeTest),
cmocka_unit_test_setup(test_bxs_valid_anywhere_error, beforeTest),
cmocka_unit_test_setup(test_bxs_valid_in_filename_error, beforeTest),
cmocka_unit_test_setup(test_bxs_free_null, beforeTest)
cmocka_unit_test_setup(test_bxs_free_null, beforeTest),
cmocka_unit_test_setup(test_bxs_concat, beforeTest),
cmocka_unit_test_setup(test_bxs_concat_nullarg, beforeTest)
};
int num_failed = 0;

View File

@ -261,4 +261,25 @@ void test_is_ascii_id_strict_invalid(void **state)
}
void test_repeat(void **state)
{
(void) state; /* unused */
char *actual = repeat(NULL, 1);
assert_null(actual);
actual = repeat("x", 0);
assert_string_equal("", actual);
BFREE(actual);
actual = repeat("x", 3);
assert_string_equal("xxx", actual);
BFREE(actual);
actual = repeat("abc", 3);
assert_string_equal("abcabcabc", actual);
BFREE(actual);
}
/* vim: set cindent sw=4: */

View File

@ -14,7 +14,7 @@
*/
/*
* Unit tests of the 'tools' module
* Unit tests of the 'tools' module.
*/
#ifndef TOOLS_TEST_H
@ -37,7 +37,9 @@ void test_is_ascii_id_invalid(void **state);
void test_is_ascii_id_strict_valid(void **state);
void test_is_ascii_id_strict_invalid(void **state);
void test_repeat(void **state);
#endif
/*EOF*/ /* vim: set cindent sw=4: */
/* vim: set cindent sw=4: */