fish-shell/src/builtin_block.cpp
Johannes Altmanninger 61486954bc Use a pager to view long outputs of builtin --help
Every builtin or function shipped with fish supports flag -h or --help to
print a slightly condensed version of its manpage.
Some of those help messages are longer than a typical screen;
this commit pipes the help to a pager to make it easier to read.

As in other places in fish we assume that either $PAGER or "less" is a
valid pager and use that.

In three places (error messages for bg, break and continue) the help is
printed to stderr instead of stdout.  To make sure the error message is
visible in the pager, we pass it to builtin_print_help, every call of which
needs to be updated.

Fixes #6227
2019-10-28 18:36:07 +01:00

140 lines
4.1 KiB
C++

// Implementation of the bind builtin.
#include "config.h" // IWYU pragma: keep
#include "builtin_block.h"
#include <stddef.h>
#include "builtin.h"
#include "common.h"
#include "event.h"
#include "fallback.h" // IWYU pragma: keep
#include "io.h"
#include "parser.h"
#include "wgetopt.h"
#include "wutil.h" // IWYU pragma: keep
enum { UNSET, GLOBAL, LOCAL };
struct block_cmd_opts_t {
int scope = UNSET;
bool erase = false;
bool print_help = false;
};
static int parse_cmd_opts(block_cmd_opts_t &opts, int *optind, //!OCLINT(high ncss method)
int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) {
wchar_t *cmd = argv[0];
static const wchar_t *const short_options = L":eghl";
static const struct woption long_options[] = {{L"erase", no_argument, NULL, 'e'},
{L"local", no_argument, NULL, 'l'},
{L"global", no_argument, NULL, 'g'},
{L"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}};
int opt;
wgetopter_t w;
while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
switch (opt) {
case 'h': {
opts.print_help = true;
break;
}
case 'g': {
opts.scope = GLOBAL;
break;
}
case 'l': {
opts.scope = LOCAL;
break;
}
case 'e': {
opts.erase = true;
break;
}
case ':': {
builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
return STATUS_INVALID_ARGS;
}
case '?': {
builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
return STATUS_INVALID_ARGS;
}
default: {
DIE("unexpected retval from wgetopt_long");
break;
}
}
}
*optind = w.woptind;
return STATUS_CMD_OK;
}
/// The block builtin, used for temporarily blocking events.
int builtin_block(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
const wchar_t *cmd = argv[0];
int argc = builtin_count_args(argv);
block_cmd_opts_t opts;
int optind;
int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams);
if (retval != STATUS_CMD_OK) return retval;
if (opts.print_help) {
builtin_print_help(parser, streams, cmd);
return STATUS_CMD_OK;
}
if (opts.erase) {
if (opts.scope != UNSET) {
streams.err.append_format(_(L"%ls: Can not specify scope when removing block\n"), cmd);
return STATUS_INVALID_ARGS;
}
if (parser.global_event_blocks.empty()) {
streams.err.append_format(_(L"%ls: No blocks defined\n"), cmd);
return STATUS_CMD_ERROR;
}
parser.global_event_blocks.pop_front();
return STATUS_CMD_OK;
}
size_t block_idx = 0;
block_t *block = parser.block_at_index(block_idx);
event_blockage_t eb = {};
switch (opts.scope) {
case LOCAL: {
// If this is the outermost block, then we're global
if (block_idx + 1 >= parser.block_count()) {
block = NULL;
}
break;
}
case GLOBAL: {
block = NULL;
break;
}
case UNSET: {
while (block != NULL && block->type() != FUNCTION_CALL &&
block->type() != FUNCTION_CALL_NO_SHADOW) {
// Set it in function scope
block = parser.block_at_index(++block_idx);
}
break;
}
default: {
DIE("unexpected scope");
break;
}
}
if (block) {
block->event_blocks.push_front(eb);
} else {
parser.global_event_blocks.push_front(eb);
}
return STATUS_CMD_OK;
}