From 8ada68ce5413eae6f30c90ae808313a04a3a9f4a Mon Sep 17 00:00:00 2001 From: Dianne Skoll Date: Tue, 11 Oct 2022 13:18:44 -0400 Subject: [PATCH] Add the "columns(str)" variant. --- examples/alignment.rem | 18 ++++++++++++++++ man/remind.1.in | 13 ++++++++++- src/calendar.c | 2 +- src/funcs.c | 49 ++++++++++++++++++++++++++++++++++++++++-- src/main.c | 2 +- src/protos.h | 2 +- tests/ansicolors.rem | 5 +++++ tests/test.cmp | 38 ++++++++++++++++++++++++++++++++ 8 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 examples/alignment.rem diff --git a/examples/alignment.rem b/examples/alignment.rem new file mode 100644 index 00000000..c06a4fa9 --- /dev/null +++ b/examples/alignment.rem @@ -0,0 +1,18 @@ +# Demo the columns() function +# +# Run as: remind -@2 alignment.rem + +SET $AddBlankLines 0 +BANNER % + +FSET center(x) pad("", " ", (columns() - columns(x))/2) + x +FSET right(x) pad("", " ", columns() - columns(x)) + x + +MSG This is left-aligned. +MSG [ansicolor(0,255,0)]This is also left-aligned.[ansicolor("")] + +MSG [center("This is centered.")] +MSG [ansicolor(0,255,0) + center("🌕 🌕 🌕 🌕 This is also centered. ") + ansicolor("")] + +msg [right("This is right-aligned.")] +msg [ansicolor(0,255,0) + right("This is also right-aligned. 🌕 🌕 🌕") + ansicolor("")] diff --git a/man/remind.1.in b/man/remind.1.in index f425c0fa..96d215f2 100644 --- a/man/remind.1.in +++ b/man/remind.1.in @@ -2828,10 +2828,21 @@ above. A \fBSTRING\fR \fIarg\fR is converted by parsing it as an integer. .RE .TP -.B columns() +.B columns([s_arg]) +If called with no arguments, \fBcolumns()\fR behaves as follows: If standard output is a TTY, returns the width of the terminal in columns. If standard output is not a TTY, attempts to open "/dev/tty" to obtain the terminal size. If this fails, returns -1. +.RS +.PP +If called with a single string argument, \fBcolumns(str)\fR returns +the number of columns \fBstr\fR will occupy if printed to a terminal. +ANSI color-changing sequences occupy zero columns whereas some Unicode +characters occupy two columns. \fBcolumns(str)\fR takes all of that +into account. Note that if Remind was compiled without Unicode support, +\fBcolumns(str)\fR returns a type mismatch error. +.RE + .TP .B current() Returns the current date and time as a DATETIME object. This may be the diff --git a/src/calendar.c b/src/calendar.c index 6826d402..c12692ad 100644 --- a/src/calendar.c +++ b/src/calendar.c @@ -9,7 +9,7 @@ /* */ /***************************************************************/ -#define _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 #include "config.h" #include "custom.h" diff --git a/src/funcs.c b/src/funcs.c index 1676b68b..75cab3af 100644 --- a/src/funcs.c +++ b/src/funcs.c @@ -13,6 +13,12 @@ #include "version.h" #include "config.h" +#ifdef REM_USE_WCHAR +#define _XOPEN_SOURCE 600 +#include +#include +#endif + #include #include @@ -230,7 +236,7 @@ BuiltinFunc Func[] = { { "char", 1, NO_MAX, 1, FChar }, { "choose", 2, NO_MAX, 1, FChoose }, { "coerce", 2, 2, 1, FCoerce }, - { "columns", 0, 0, 0, FColumns }, + { "columns", 0, 1, 0, FColumns }, { "current", 0, 0, 0, FCurrent }, { "date", 3, 3, 1, FDate }, { "datepart", 1, 1, 1, FDatepart }, @@ -3409,5 +3415,44 @@ static int FRows(func_info *info) } static int FColumns(func_info *info) { - return rows_or_cols(info, 0); +#ifdef REM_USE_WCHAR + size_t len; + wchar_t *buf, *s; + int width; +#endif + if (Nargs == 0) { + return rows_or_cols(info, 0); + } + ASSERT_TYPE(0, STR_TYPE); +#ifdef REM_USE_WCHAR + len = mbstowcs(NULL, ARGSTR(0), 0); + if (len == (size_t) -1) return E_NO_MEM; + buf = calloc(len+1, sizeof(wchar_t)); + if (!buf) return E_NO_MEM; + (void) mbstowcs(buf, ARGSTR(0), len+1); + + s = buf; + width = 0; + while (*s) { + if (*s == 0x1B && *(s+1) == '[') { + /* Skip escape sequences */ + s += 2; + while (*s && (*s < 0x40 || *s > 0x7E)) { + s++; + } + if (*s) { + s++; + } + continue; + } + width += wcwidth(*s); + s++; + } + free(buf); + RetVal.type = INT_TYPE; + RETVAL = width; + return OK; +#else + return E_BAD_TYPE; +#endif } diff --git a/src/main.c b/src/main.c index 92750a66..65fae32a 100644 --- a/src/main.c +++ b/src/main.c @@ -10,7 +10,7 @@ /* */ /***************************************************************/ -#define _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 #include "config.h" #include diff --git a/src/protos.h b/src/protos.h index 426f5598..1e39923f 100644 --- a/src/protos.h +++ b/src/protos.h @@ -178,7 +178,7 @@ int have_callstack(void); int print_callstack(FILE *fp); void pop_call(void); #ifdef REM_USE_WCHAR -#define _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 #include #include void PutWideChar(wchar_t const wc); diff --git a/tests/ansicolors.rem b/tests/ansicolors.rem index 50c684d9..6f195d09 100644 --- a/tests/ansicolors.rem +++ b/tests/ansicolors.rem @@ -42,3 +42,8 @@ set a ansicolor(128, 128, 128, -1) set a ansicolor(128, 128, 128, 0, 2) set a ansicolor(128, 128, 128, 0, -1) +set a ansicolor(128,0,0) + +set str a + "foo: 🌅" +set w columns(str) +MSG Width of [str] is: [w] diff --git a/tests/test.cmp b/tests/test.cmp index 7b0c258e..3c184034 100644 --- a/tests/test.cmp +++ b/tests/test.cmp @@ -5568,6 +5568,8 @@ keyword will wrap it so it's pleasantly readable. ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: -1 UseVTColors is: 1 Use256Colors is: 0 @@ -5629,6 +5631,8 @@ keyword will wrap it so ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: -1 UseVTColors is: 1 Use256Colors is: 1 @@ -5690,6 +5694,8 @@ keyword will wrap it[ ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: -1 UseVTColors is: 1 Use256Colors is: 0 @@ -5751,6 +5757,8 @@ keyword will[38;2;0 ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: 0 UseVTColors is: 1 Use256Colors is: 0 @@ -5812,6 +5820,8 @@ keyword will wrap it so ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: 0 UseVTColors is: 1 Use256Colors is: 1 @@ -5873,6 +5883,8 @@ keyword will wrap it[ ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: 0 UseVTColors is: 1 Use256Colors is: 0 @@ -5934,6 +5946,8 @@ keyword will[38;2;0 ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: 1 UseVTColors is: 1 Use256Colors is: 0 @@ -5995,6 +6009,8 @@ keyword will wrap it so ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: 1 UseVTColors is: 1 Use256Colors is: 1 @@ -6056,6 +6072,8 @@ keyword will wrap it[ ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: 1 UseVTColors is: 1 Use256Colors is: 0 @@ -6117,6 +6135,8 @@ keyword will[38;2;0 ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: -1 UseVTColors is: 1 Use256Colors is: 0 @@ -6178,6 +6198,8 @@ keyword will wrap it so ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: -1 UseVTColors is: 1 Use256Colors is: 1 @@ -6239,6 +6261,8 @@ keyword will wrap it[ ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: -1 UseVTColors is: 1 Use256Colors is: 0 @@ -6300,6 +6324,8 @@ keyword will[38;2;0 ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: 0 UseVTColors is: 1 Use256Colors is: 0 @@ -6361,6 +6387,8 @@ keyword will wrap it so ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: 0 UseVTColors is: 1 Use256Colors is: 1 @@ -6422,6 +6450,8 @@ keyword will wrap it[ ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: 0 UseVTColors is: 1 Use256Colors is: 0 @@ -6483,6 +6513,8 @@ keyword will[38;2;0 ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: 1 UseVTColors is: 1 Use256Colors is: 0 @@ -6544,6 +6576,8 @@ keyword will wrap it so ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: 1 UseVTColors is: 1 Use256Colors is: 1 @@ -6605,6 +6639,8 @@ keyword will wrap it[ ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + TerminalBackground is: 1 UseVTColors is: 1 Use256Colors is: 0 @@ -6666,6 +6702,8 @@ keyword will[38;2;0 ../tests/ansicolors.rem(41): ansicolor(): Number too low ../tests/ansicolors.rem(42): ansicolor(): Number too high ../tests/ansicolors.rem(43): ansicolor(): Number too low +Width of foo: 🌅 is: 7 + $AddBlankLines test Reminders for Saturday, 1st January, 2022: