From c8e1894c720497022a6fea9f2f61023fea56aa20 Mon Sep 17 00:00:00 2001 From: Andrew Prokhorenkov Date: Wed, 17 Jun 2020 23:11:03 -0500 Subject: [PATCH] builtin_string: add pad command --- doc_src/cmds/string-pad.rst | 42 ++++++++++++++++++++++++++ src/builtin_string.cpp | 60 ++++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 doc_src/cmds/string-pad.rst diff --git a/doc_src/cmds/string-pad.rst b/doc_src/cmds/string-pad.rst new file mode 100644 index 000000000..721e78b49 --- /dev/null +++ b/doc_src/cmds/string-pad.rst @@ -0,0 +1,42 @@ +string-pad - pad characters before and after string +======================================== + +Synopsis +-------- + +.. BEGIN SYNOPSIS + +:: + + string pad [(-l | --left)] [(-r | --right)] [(-c | --char) CHAR] [(-n | --count) INTEGER] [(-q | --quiet)] [STRING...] + +.. END SYNOPSIS + +Description +----------- + +.. BEGIN DESCRIPTION + +``string pad`` pads before and after the string specified character for each STRING. If ``-l`` or ``--left`` is given, only padded before string. Left only is the default padding. If ``-r`` or ``--right`` is given, only padded after string. The ``-c`` or ``--char`` switch causes the characters in CHAR to be padded instead of whitespace. The ``-n`` or ``--count`` integer specifies the amount of characters to be padded. The default padding count is 0. Exit status: 0 if string was padded, or 1 otherwise. + +.. END DESCRIPTION + +Examples +-------- + +.. BEGIN EXAMPLES + +:: + + >_ string pad -l -n 10 -c ' ' 'abc' + abc + + >_ string pad --right --count 5 --char=z foo bar + foozzzzz + barzzzzz + + >_ string pad --left --right -n 5 --char=- foo + -----foo----- + + +.. END EXAMPLES diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 429676f79..66771649c 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -135,7 +135,8 @@ class arg_iterator_t { // valid and get the result of parsing the command for flags. using options_t = struct options_t { //!OCLINT(too many fields) bool all_valid = false; - bool chars_valid = false; + bool char_to_pad_valid = false; + bool chars_to_trim_valid = false; bool count_valid = false; bool entire_valid = false; bool filter_valid = false; @@ -180,6 +181,8 @@ using options_t = struct options_t { //!OCLINT(too many fields) long start = 0; long end = 0; + wchar_t char_to_pad = ' '; + std::vector fields; const wchar_t *chars_to_trim = L" \f\n\r\t\v"; @@ -242,9 +245,16 @@ static int handle_flag_a(wchar_t **argv, parser_t &parser, io_streams_t &streams static int handle_flag_c(wchar_t **argv, parser_t &parser, io_streams_t &streams, const wgetopter_t &w, options_t *opts) { - if (opts->chars_valid) { + if (opts->chars_to_trim_valid) { opts->chars_to_trim = w.woptarg; return STATUS_CMD_OK; + } else if (opts->char_to_pad_valid) { + if (wcslen(w.woptarg) != 1) { + string_error(streams, _(L"%ls: Padding should be a character '%ls'\n"), argv[0], w.woptarg); + return STATUS_INVALID_ARGS; + } + opts->char_to_pad = w.woptarg[0]; + return STATUS_CMD_OK; } string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return STATUS_INVALID_ARGS; @@ -457,7 +467,8 @@ static int handle_flag_v(wchar_t **argv, parser_t &parser, io_streams_t &streams static wcstring construct_short_opts(options_t *opts) { //!OCLINT(high npath complexity) wcstring short_opts(L":"); if (opts->all_valid) short_opts.append(L"a"); - if (opts->chars_valid) short_opts.append(L"c:"); + if (opts->char_to_pad_valid) short_opts.append(L"c:"); + if (opts->chars_to_trim_valid) short_opts.append(L"c:"); if (opts->count_valid) short_opts.append(L"n:"); if (opts->entire_valid) short_opts.append(L"e"); if (opts->filter_valid) short_opts.append(L"f"); @@ -1245,6 +1256,46 @@ static int string_collect(parser_t &parser, io_streams_t &streams, int argc, wch return appended > 0 ? STATUS_CMD_OK : STATUS_CMD_ERROR; } +static int string_pad(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { + options_t opts; + opts.char_to_pad_valid = true; + opts.count_valid = true; + opts.left_valid = true; + opts.right_valid = true; + opts.quiet_valid = true; + int optind; + int retval = parse_opts(&opts, &optind, 0, argc, argv, parser, streams); + if (retval != STATUS_CMD_OK) return retval; + + // If neither left or right is specified, we pad only on the left. + if (!opts.left && !opts.right) { + opts.left = true; + opts.right = false; + } + + size_t npad = 0; + + arg_iterator_t aiter(argv, optind, streams); + while (const wcstring *arg = aiter.nextstr()) { + size_t begin = 0, end = arg->size(); + wcstring padded_arg = wcstring(*arg, 0, arg->size()); + if (opts.right) { + padded_arg.append(opts.count, opts.char_to_pad); + } + if (opts.left) { + padded_arg.insert(0, opts.count, opts.char_to_pad); + } + // assert(begin <= end && end <= arg->size()); + npad += arg->size() - (end - begin); + if (!opts.quiet) { + streams.out.append(padded_arg); + streams.out.append(L'\n'); + } + } + + return npad > 0 ? STATUS_CMD_OK : STATUS_CMD_ERROR; +} + // Helper function to abstract the repeat logic from string_repeat // returns the to_repeat string, repeated count times. static wcstring wcsrepeat(const wcstring &to_repeat, size_t count) { @@ -1363,7 +1414,7 @@ static int string_sub(parser_t &parser, io_streams_t &streams, int argc, wchar_t static int string_trim(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) { options_t opts; - opts.chars_valid = true; + opts.chars_to_trim_valid = true; opts.left_valid = true; opts.right_valid = true; opts.quiet_valid = true; @@ -1448,6 +1499,7 @@ string_subcommands[] = { {L"split", &string_split}, {L"split0", &string_split0}, {L"sub", &string_sub}, {L"trim", &string_trim}, {L"lower", &string_lower}, {L"upper", &string_upper}, {L"repeat", &string_repeat}, {L"unescape", &string_unescape}, {L"collect", &string_collect}, + {L"pad", &string_pad}, {nullptr, nullptr}}; /// The string builtin, for manipulating strings.