From 93316d754c7c1dfc3c935f077d8c5842f3202bda Mon Sep 17 00:00:00 2001 From: Dianne Skoll Date: Wed, 24 Dec 2025 16:05:25 -0500 Subject: [PATCH] Add the mbpad function. --- man/remind.1.in | 9 ++- src/funcs.c | 121 ++++++++++++++++++++++++++++++++++++ tests/manpage-personal-dict | 1 + tests/test.cmp | 11 +++- tests/test.rem | 10 +++ 5 files changed, 149 insertions(+), 3 deletions(-) diff --git a/man/remind.1.in b/man/remind.1.in index 9c8b1153..b62c81d8 100644 --- a/man/remind.1.in +++ b/man/remind.1.in @@ -4529,8 +4529,8 @@ any longer. .TP .B pad(x_arg, s_padstr, i_len [, i_right]) Converts the first argument \fIarg\fR to a string if necessary, and -then if it is shorter than \fIlen\fR characters, pads to to -\fIlen\fR characters using as many copies (including partial copies) +then if it is shorter than \fIlen\fR bytes, pads to to +\fIlen\fR bytes using as many copies (including partial copies) of \fIpadstr\fR as necessary. By default, the string is left-padded, but if \fIright\fR is supplied and non-zero, the string will be right-padded. @@ -4547,6 +4547,11 @@ Here are some examples: .fi .RE +.TP +.B mbpad(x_arg, s_padstr, i_len [, i_right]) +This is the multibyte counterpart to \fBpad\fR. The length is +specified in characters rather than bytes. Use \fBmbpad\fR rather +than \fBpad\fR if either of the strings contains non-ASCII characters. .TP .B plural(i_num [,s_str1 [,s_str2]]) Can take from one to three arguments. If one argument is supplied, returns diff --git a/src/funcs.c b/src/funcs.c index 961160d1..fca18eeb 100644 --- a/src/funcs.c +++ b/src/funcs.c @@ -140,6 +140,7 @@ static int FLower (func_info *); static int FMax (func_info *); static int FMbchar (func_info *); static int FMbindex (func_info *); +static int FMbpad (func_info *); static int FMbstrlen (func_info *); static int FMbsubstr (func_info *); static int FMin (func_info *); @@ -326,6 +327,7 @@ BuiltinFunc Func[] = { { "max", 1, NO_MAX, 1, FMax, NULL }, { "mbchar", 1, NO_MAX, 1, FMbchar, NULL }, { "mbindex", 2, 3, 1, FMbindex, NULL }, + { "mbpad", 3, 4, 1, FMbpad, NULL }, { "mbstrlen", 1, 1, 1, FMbstrlen, NULL }, { "mbsubstr", 2, 3, 1, FMbsubstr, NULL }, { "min", 1, NO_MAX, 1, FMin, NULL }, @@ -1430,6 +1432,125 @@ static int FPad(func_info *info) return r; } +/***************************************************************/ +/* */ +/* FMbpad - multibyte version of Fpad */ +/* */ +/***************************************************************/ +static int FMbpad(func_info *info) +{ + int r; + wchar_t *s; + wchar_t *d; + + size_t len; + size_t len2; + size_t wantlen; + size_t i; + + wchar_t *src; + wchar_t *pad; + wchar_t *dest; + + char *result; + ASSERT_TYPE(1, STR_TYPE); + ASSERT_TYPE(2, INT_TYPE); + if (Nargs == 4) { + ASSERT_TYPE(3, INT_TYPE); + } + + if (ARG(0).type != STR_TYPE) { + r = DoCoerce(STR_TYPE, &ARG(0)); + if (r != OK) return r; + } + + wantlen = ARGV(2); + + /* Convert ARGV(0) and ARGV(1) to wide-char strings */ + len = mbstowcs(NULL, ARGSTR(0), 0); + if (len == (size_t) -1) { + return E_BAD_MB_SEQ; + } + + if (len >= wantlen) { + DCOPYVAL(RetVal, ARG(0)); + return OK; + } + + if (strlen(ARGSTR(1)) == 0) { + return E_BAD_TYPE; + } + + if (MaxStringLen > 0 && wantlen > (size_t) MaxStringLen) { + return E_STRING_TOO_LONG; + } + + src = calloc(len+1, sizeof(wchar_t)); + if (!src) { + return E_NO_MEM; + } + (void) mbstowcs(src, ARGSTR(0), len+1); + len2 = mbstowcs(NULL, ARGSTR(1), 0); + if (len == (size_t) -1) { + free(src); + return E_BAD_MB_SEQ; + } + pad = calloc(len2+1, sizeof(wchar_t)); + if (!pad) { + free(src); + return E_NO_MEM; + } + (void) mbstowcs(pad, ARGSTR(1), len2+1); + + dest = calloc(wantlen+1, sizeof(wchar_t)); + if (!dest) { + free(src); + free(pad); + return E_NO_MEM; + } + + d = dest; + if (Nargs < 4 || !ARGV(3)) { + /* Pad on the LEFT */ + s = pad; + for (i=0; i "Av" ivritmon(1991-07-12) => "אב" hebmon(1991-08-11) => "Elul" ivritmon(1991-08-11) => "אלול" +mbpad("foo", "bar", 3) => "foo" +mbpad("foo", "bar", 3, 1) => "foo" +mbpad("foo", "bar", 8) => "barbafoo" +mbpad("foo", "bar", 8, 1) => "foobarba" +mbpad("foo", "🙂💩", 3) => "foo" +mbpad("foo", "🙂💩", 3, 1) => "foo" +mbpad("foo", "🙂💩", 8) => "🙂💩🙂💩🙂foo" +mbpad("foo", "🙂💩", 8, 1) => "foo🙂💩🙂💩🙂" Variable hash table statistics: Entries: 100146; Buckets: 87719; Non-empty Buckets: 66303 Maxlen: 5; Minlen: 0; Avglen: 1.142; Stddev: 0.878; Avg nonempty len: 1.510 @@ -16732,7 +16740,7 @@ Expression nodes high-water: 302076 Expression nodes leaked: 0 Parse level high-water: 34 Max expr node evaluations per line: 2001 -Total expression node evaluations: 106607 +Total expression node evaluations: 106643 Test 2 @@ -24717,6 +24725,7 @@ lower max mbchar mbindex +mbpad mbstrlen mbsubstr min diff --git a/tests/test.rem b/tests/test.rem index bf725f2b..0de6587c 100644 --- a/tests/test.rem +++ b/tests/test.rem @@ -1853,6 +1853,16 @@ set a ivritmon('1991-07-12') set a hebmon('1991-08-11') set a ivritmon('1991-08-11') +# mbpad + +set a mbpad("foo", "bar", 3) +set a mbpad("foo", "bar", 3, 1) +set a mbpad("foo", "bar", 8) +set a mbpad("foo", "bar", 8, 1) +set a mbpad("foo", "🙂💩", 3) +set a mbpad("foo", "🙂💩", 3, 1) +set a mbpad("foo", "🙂💩", 8) +set a mbpad("foo", "🙂💩", 8, 1) DEBUG -x # Don't want Remind to queue reminders EXIT