Add long options capability to command line

This commit is contained in:
Thomas Jensen 2023-05-06 22:43:11 +02:00
parent 2910b961c1
commit 08feb7eb2c
No known key found for this signature in database
GPG Key ID: A4ACEE270D0FB7DB
8 changed files with 163 additions and 84 deletions

View File

@ -33,7 +33,7 @@ Options offered by
.I boxes
are the following:
.TP 0.6i
.B \-a \fIstring\fP
\fB\-a\fP \fIstring\fP, \fB\-\-align\fP=\fIstring\fP
Alignment/positioning of text inside box. This option takes a format string
argument which is read from left to right. The format string may not
contain whitespace and must consist of one or more of the following
@ -41,8 +41,8 @@ components:
.br
.I h\fPx
\- horizontal alignment of the input text block inside a potentially larger
box. Possible values for
\- horizontal alignment of the input text block inside a box. Possible values
for
.I x
are
.I l
@ -56,8 +56,8 @@ input text block (use the
argument instead).
.br
.I v\fPx
\- vertical alignment of the input text block inside a potentially larger
box. Possible values for
\- vertical alignment of the input text block inside a box. Possible values
for
.I x
are
.I t
@ -99,13 +99,13 @@ Short hand notations (can be combined with the above arguments):
.I h\fPr\fIv\fPc\fIj\fPr
.br
The factory default setting for
The default for
.B \-a
is
.I h\fPl\fIv\fPt.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-c \fIstring\fP
\fB\-c\fP \fIstring\fP, \fB\-\-create\fP=\fIstring\fP
Command line design definition for simple cases. The argument of this
option is the definition for the "west" (W) shape. The defined shape must
consist of exactly one line, i.e. no multi\-line shapes are allowed. The
@ -127,12 +127,12 @@ may of course be used in conjunction with any of the other options. By default,
is not specified.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-d \fIstring\fP
\fB\-d\fP \fIstring\fP, \fB\-\-design\fP=\fIstring\fP
Design selection. The one argument of this option is the name of the design to
use, which may either be a design's primary name or any of its alias names.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-e \fIeol\fP
\fB\-e\fP \fIeol\fP, \fB\-\-eol\fP=\fIeol\fP
Override line terminator.
.I eol
can be
@ -151,7 +151,7 @@ Let us know in <URL:https://github.com/ascii-boxes/boxes/issues/60> if you
think we should keep it.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-f \fIstring\fP
\fB\-f\fP \fIstring\fP, \fB\-\-config\fP=\fIstring\fP
Use alternate config file. The one argument of this option is the name of a
valid
.I boxes
@ -161,13 +161,11 @@ can also be a directory which contains a configuration file. More information
on this topic below in the CONFIGURATION FILE section.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-\-help
.TQ
.B \-h
\fB\-h\fP, \fB\-\-help\fP
Print usage information.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-i \fIstring\fP
\fB\-i\fP \fIstring\fP, \fB\-\-indent\fP=\fIstring\fP
Indentation mode. Possible arguments are
.I text
(indent text inside of box),
@ -178,7 +176,7 @@ Indentation mode. Possible arguments are
.I box\fP.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-k \fIbool\fP
\fB\-k\fP \fIbool\fP, \fB\-\-kill\-blank\fP, \fB\-\-no\-kill\-blank\fP
Kill leading/trailing blank lines on removal. The value of
.I bool
is either
@ -201,15 +199,16 @@ part, the default is
.I true\fP.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-l
(ell) List designs. Produces a listing of all available box designs in the
\fB\-l\fP, \fB\-\-list\fP
List designs. Produces a listing of all available box designs in the
config file, along with a sample box and information about its creator.
Also checks the syntax of the entire config file. If used together with
Also checks the syntax of the entire config file. If used in conjunction
with
.B \-d\fP,
displays detailed information about the specified design.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-m
\fB\-m\fP, \fB\-\-mend\fP
Mend box. This removes a (potentially broken) box as with
.B \-r\fP,
and redraws it afterwards. The mended box is drawn according to the
@ -219,7 +218,7 @@ padding, indentation, etc. for the mended box. Implies
.I false\fP.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-n \fIencoding\fP
\fB\-n\fP \fIencoding\fP, \fB\-\-encoding\fP=\fIencoding\fP
Character encoding. Overrides the character encoding of the input and output
text. Choose from the list shown by \fIiconv -l\fP. If an invalid character
encoding is specified here, \fIUTF-8\fP is used as a fallback. The default
@ -227,12 +226,13 @@ is to use the system encoding, which is normally the best course of action.
So don't specify this option unless you have to.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-p \fIstring\fP
\fB\-p\fP \fIstring\fP, \fB\-\-padding\fP=\fIstring\fP
Padding. Specify padding in spaces around the input text block for all
sides of the box. The argument string may not contain whitespace and must
consist of a combination of the following characters, each followed by a
number indicating the padding in spaces:
.br
.I a
\- (all) give padding for all sides at once
.br
@ -254,6 +254,7 @@ number indicating the padding in spaces:
.I r
\- (right) give padding for right (east) side
.br
Example:
.B \-p
.I a\fP4\fIt\fP2
@ -261,11 +262,11 @@ would define the padding to be 4 characters on all sides, except for the
top of the box, where the input text block will be only 2 lines away from
the box.
.br
By default, unless specified otherwise in the config file, no padding is
used.
The default for this option is determined by the box design used. If the
design does not specify anything, no default padding is used.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-q \fIquery\fP
\fB\-q\fP \fIquery\fP, \fB\-\-tag\-query\fP=\fIquery\fP
Query designs by tag. In contrast to
.B \-l\fP,
this will only print the matching design names. This option is normally used
@ -278,7 +279,9 @@ in order to match. A tag may optionally be prefixed with
in order to require that it be present, or with
.I \-
in order to exclude designs which have that tag. Each tag can only occur once
per query.
per query. The special query
.I (all)
matches all box designs, even if they don't have any tags.
.br
This option is intended for use by scripts. Alias names are printed below
their primary design name, and postfixed with
@ -288,15 +291,15 @@ Example:
.I boxes -q programming,-comment
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-r
\fB\-r\fP, \fB--remove\fP
Remove an existing box. Which design to use is detected automatically. In
order to save time or in case the detection does not decide correctly, combine
with
.B \-d
to specify the design. The default is to draw a new box.
to specify the design.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-s \fIwidth\fBx\fIheight
\fB\-s\fP \fIwidth\fP\fBx\fP\fIheight\fP, \fB\-\-size\fP=\fIwidth\fP\fBx\fP\fIheight\fP
Box size. This option specifies the desired box size in units of columns
(for width) and lines (for height).
If only a single number is given as argument, this number specifies the
@ -309,20 +312,17 @@ influence the box size (such as
By default, the smallest possible box is created around the text.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-t \fIstring\fP
\fB\-t\fP \fIstring\fP, \fB\-\-tabs\fP=\fIstring\fP
Tab handling. This option controls how tab characters in the input text are
handled. The option string must always begin with a
.I uint
number indicating the distance between tab stops. It is important that this
value be set correctly, or tabulator characters will upset your input text.
The correct tab distance value depends on the settings used for the text
you are processing. A common value is 8.
handled. The option string must always begin with a positive integer number
indicating the distance between tab stops, sometimes called "spaces per tab".
.br
Immediately following the tab distance, an optional character can be appended,
telling
.I boxes
how to treat the leading tabs. The following options are available:
.br
.B e
\- expand tabs into spaces
.br
@ -335,13 +335,12 @@ how to treat the leading tabs. The following options are available:
turn as many spaces as possible into tabs.
.br
In order to maintain backwards compatibility, the
The
.B \-t
.I string
can be just a number. In that case,
.B e
is assumed for tab handling, which removes all tabs and replaces them with
spaces. The factory default for the
is assumed for tab handling. The factory default for the
.B \-t
option is simply 8, which is just such a case.
.br
@ -351,7 +350,7 @@ in order to have your leading tabs unexpanded. In the box content, tabs are
always converted into spaces. The tab distance in this example is 4.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i
.B \-v
\fB\-v\fP, \fB\-\-version\fP
Print out current version number.
.\" =======================================================================
.SH CONFIGURATION FILE

View File

@ -110,6 +110,7 @@ typedef struct { /* Command line options: */
char halign; /** `-a`: ( h[lcr]v[tcb] ) */
char justify; /** `-a`: 'l', 'c', 'r', or '\0' */
char *cld; /** `-c`: commandline design definition */
int color; /** `--color` or `--no-color`: `force_monochrome`, `color_from_terminal`, or `force_ansi_color` */
design_t *design; /** `-d`: currently used box design */
int design_choice_by_user; /** `-d`, `-c`: true if design was chosen by user */
char *eol; /** `-e`: line break to use. Never NULL, default to "\n". */
@ -135,6 +136,11 @@ typedef struct { /* Command line options: */
extern opt_t opt;
/* The possible values of the `color` field from `opt_t`: */
#define force_monochrome 0
#define color_from_terminal 1
#define force_ansi_color 2
typedef struct {
size_t len; /* length of visible text in columns (visible character positions in a text terminal), which is the same as the length of the 'text' field */

View File

@ -19,6 +19,7 @@
#include "config.h"
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
@ -81,25 +82,27 @@ void usage_long(FILE *st)
fprintf(st, "%s - draws any kind of box around your text (or removes it)\n", PROJECT);
fprintf(st, " Website: https://boxes.thomasjensen.com/\n");
fprintf(st, "Usage: %s [options] [infile [outfile]]\n", PROJECT);
fprintf(st, " -a fmt alignment/positioning of text inside box [default: hlvt]\n");
fprintf(st, " -c str use single shape box design where str is the W shape\n");
fprintf(st, " -d name box design [default: first one in file]\n");
fprintf(st, " -e eol Override line break type (experimental) [default: %s]\n",
strcmp(EOL_DEFAULT, "\r\n") == 0 ? "CRLF" : "LF");
fprintf(st, " -f file configuration file [default: %s]\n",
config_file != NULL ? bxs_to_output(config_file) : "none");
fprintf(st, " -h print usage information\n");
fprintf(st, " -i mode indentation mode [default: box]\n");
fprintf(st, " -k bool leading/trailing blank line retention on removal\n");
fprintf(st, " -l list available box designs w/ samples\n");
fprintf(st, " -m mend (repair) box\n");
fprintf(st, " -n enc Character encoding of input and output [default: %s]\n", locale_charset());
fprintf(st, " -p fmt padding [default: none]\n");
fprintf(st, " -q qry query the list of designs by tag\n"); /* with "(undoc)" as query, trigger undocumented webui stuff instead */
fprintf(st, " -r remove box\n");
fprintf(st, " -s wxh box size (width w and/or height h)\n");
fprintf(st, " -t str tab stop distance and expansion [default: %de]\n", DEF_TABSTOP);
fprintf(st, " -v print version information\n");
fprintf(st, " -a, --align <fmt> Alignment/positioning of text inside box [default: hlvt]\n");
fprintf(st, " -c, --create <str> Use single shape box design where str is the W shape\n");
fprintf(st, " -d, --design <name> Box design [default: first one in file]\n");
fprintf(st, " -e, --eol <eol> Override line break type (experimental) [default: %s]\n",
strcmp(EOL_DEFAULT, "\r\n") == 0 ? "CRLF" : "LF");
fprintf(st, " -f, --config <file> Configuration file [default: %s]\n",
config_file != NULL ? bxs_to_output(config_file) : "none");
fprintf(st, " -h, --help Print usage information\n");
fprintf(st, " -i, --indent <mode> Indentation mode [default: box]\n");
fprintf(st, " -k <bool> Leading/trailing blank line retention on removal\n");
fprintf(st, " --kill-blank Kill leading/trailing blank lines on removal (like -k true)\n");
fprintf(st, " --no-kill-blank Retain leading/trailing blank lines on removal (like -k false)\n");
fprintf(st, " -l, --list List available box designs w/ samples\n");
fprintf(st, " -m, --mend Mend (repair) box\n");
fprintf(st, " -n, --encoding <enc> Character encoding of input and output [default: %s]\n", locale_charset());
fprintf(st, " -p, --padding <fmt> Padding [default: none]\n");
fprintf(st, " -q, --tag-query <qry> Query the list of designs by tag\n"); /* with "(undoc)" as query, trigger undocumented webui stuff instead */
fprintf(st, " -r, --remove Remove box\n");
fprintf(st, " -s, --size <wxh> Box size (width w and/or height h)\n");
fprintf(st, " -t, --tabs <str> Tab stop distance and expansion [default: %de]\n", DEF_TABSTOP);
fprintf(st, " -v, --version Print version information\n");
bxs_free(config_file);
}
@ -110,7 +113,8 @@ static opt_t *create_new_opt()
{
opt_t *result = (opt_t *) calloc(1, sizeof(opt_t));
if (result != NULL) {
/* all valued initialized with 0 or NULL */
/* all values initialized with 0 or NULL */
result->color = color_from_terminal;
result->tabstop = DEF_TABSTOP;
result->eol = "\n"; /* we must default to "\n" instead of EOL_DEFAULT as long as stdout is in text mode */
result->tabexp = 'e';
@ -623,17 +627,41 @@ opt_t *process_commandline(int argc, char *argv[])
#endif
opt_t *result = create_new_opt();
optind = 1; /* ensure that getopt() will process all arguments, even in unit test situations */
/* Intercept '--help' and '-?' cases first, as they are not supported by getopt() */
if (argc >= 2 && argv[1] != NULL && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)) {
/* Intercept '-?' case first, as it is not supported by getopt_long() */
if (argc >= 2 && argv[1] != NULL && strcmp(argv[1], "-?") == 0) {
result->help = 1;
return result;
}
optind = 1; /* ensure that getopt() will process all arguments, even in unit test situations */
int option_index = 0;
const struct option long_options[] = {
{ "align", required_argument, NULL, 'a' },
{ "create", required_argument, NULL, 'c' },
{ "design", required_argument, NULL, 'd' },
{ "eol", required_argument, NULL, 'e' },
{ "config", required_argument, NULL, 'f' },
{ "help", no_argument, NULL, 'h' },
{ "indent", required_argument, NULL, 'i' },
{ "kill-blank", no_argument, NULL, OPT_KILLBLANK },
{ "no-kill-blank", no_argument, NULL, OPT_NO_KILLBLANK },
{ "list", no_argument, NULL, 'l' },
{ "mend", no_argument, NULL, 'm' },
{ "encoding", required_argument, NULL, 'n' },
{ "padding", required_argument, NULL, 'p' },
{ "tag-query", required_argument, NULL, 'q' },
{ "remove", no_argument, NULL, 'r' },
{ "size", required_argument, NULL, 's' },
{ "tabs", required_argument, NULL, 't' },
{ "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0 }
};
const char *short_options = "a:c:d:e:f:hi:k:lmn:p:q:rs:t:v";
int oc; /* option character */
do {
oc = getopt(argc, argv, "a:c:d:e:f:hi:k:lmn:p:q:rs:t:v");
oc = getopt_long(argc, argv, short_options, long_options, &option_index);
switch (oc) {
@ -648,7 +676,7 @@ opt_t *process_commandline(int argc, char *argv[])
return NULL;
}
break;
case 'd':
if (design_choice(result, optarg) != 0) {
return NULL;
@ -680,6 +708,18 @@ opt_t *process_commandline(int argc, char *argv[])
return NULL;
}
break;
case OPT_KILLBLANK:
if (result->killblank == -1) {
result->killblank = 1;
}
break;
case OPT_NO_KILLBLANK:
if (result->killblank == -1) {
result->killblank = 0;
}
break;
case 'l':
result->l = 1; /* list available box styles */

View File

@ -20,6 +20,18 @@
#ifndef CMDLINE_H
#define CMDLINE_H
#include <stdio.h>
#include "boxes.h"
/*
* Some ints that define return values for getopt_long() to indicate certain long options.
*/
#define OPT_KILLBLANK 1003
#define OPT_NO_KILLBLANK 1004
/**
* Print usage information on stream `st`, including a header text.

View File

@ -2,28 +2,32 @@
--help
:INPUT
:OUTPUT-FILTER
s/^(.+?-e eol.+?default: )\w+]/\1EOL_DEFAULT]/
s/^(.+?-f file.+?default: ).*/\1GLOBAL_CONFIG]/
s/^(.+?-n enc.+?default: ).*/\1CHARSET_DEFAULT]/
s/^(.+?-e, --eol .+?default: )\w+]/\1EOL_DEFAULT]/
s/^(.+?-f, --config .+?default: ).*/\1GLOBAL_CONFIG]/
s/^(.+?-n, --encoding .+?default: ).*/\1CHARSET_DEFAULT]/
:EXPECTED
boxes - draws any kind of box around your text (or removes it)
Website: https://boxes.thomasjensen.com/
Usage: boxes [options] [infile [outfile]]
-a fmt alignment/positioning of text inside box [default: hlvt]
-c str use single shape box design where str is the W shape
-d name box design [default: first one in file]
-e eol Override line break type (experimental) [default: EOL_DEFAULT]
-f file configuration file [default: GLOBAL_CONFIG]
-h print usage information
-i mode indentation mode [default: box]
-k bool leading/trailing blank line retention on removal
-l list available box designs w/ samples
-m mend (repair) box
-n enc Character encoding of input and output [default: CHARSET_DEFAULT]
-p fmt padding [default: none]
-q qry query the list of designs by tag
-r remove box
-s wxh box size (width w and/or height h)
-t str tab stop distance and expansion [default: 8e]
-v print version information
-a, --align <fmt> Alignment/positioning of text inside box [default: hlvt]
-c, --create <str> Use single shape box design where str is the W shape
--color Force output of ANSI sequences if present
--no-color Force monochrome output (no ANSI sequences)
-d, --design <name> Box design [default: first one in file]
-e, --eol <eol> Override line break type (experimental) [default: EOL_DEFAULT]
-f, --config <file> Configuration file [default: GLOBAL_CONFIG]
-h, --help Print usage information
-i, --indent <mode> Indentation mode [default: box]
-k <bool> Leading/trailing blank line retention on removal
--kill-blank Kill leading/trailing blank lines on removal (like -k true)
--no-kill-blank Retain leading/trailing blank lines on removal (like -k false)
-l, --list List available box designs w/ samples
-m, --mend Mend (repair) box
-n, --encoding <enc> Character encoding of input and output [default: CHARSET_DEFAULT]
-p, --padding <fmt> Padding [default: none]
-q, --tag-query <qry> Query the list of designs by tag
-r, --remove Remove box
-s, --size <wxh> Box size (width w and/or height h)
-t, --tabs <str> Tab stop distance and expansion [default: 8e]
-v, --version Print version information
:EOF

View File

@ -160,6 +160,22 @@ void test_killblank_multiple(void **state)
}
void test_killblank_long(void **state)
{
UNUSED(state);
opt_t *actual = act(2, "--kill-blank", "--remove");
assert_non_null(actual);
assert_int_equal(1, actual->killblank);
actual = act(2, "--no-kill-blank", "--remove");
assert_non_null(actual);
assert_int_equal(0, actual->killblank);
}
void test_padding_top_bottom(void **state)
{
UNUSED(state);

View File

@ -31,6 +31,7 @@ void test_killblank_true(void **state);
void test_killblank_false(void **state);
void test_killblank_invalid(void **state);
void test_killblank_multiple(void **state);
void test_killblank_long(void **state);
void test_padding_top_bottom(void **state);
void test_padding_invalid(void **state);

View File

@ -58,6 +58,7 @@ int main(void)
cmocka_unit_test_setup(test_killblank_false, beforeTest),
cmocka_unit_test_setup(test_killblank_invalid, beforeTest),
cmocka_unit_test_setup(test_killblank_multiple, beforeTest),
cmocka_unit_test_setup(test_killblank_long, beforeTest),
cmocka_unit_test_setup(test_padding_top_bottom, beforeTest),
cmocka_unit_test_setup(test_padding_invalid, beforeTest),
cmocka_unit_test_setup(test_padding_negative, beforeTest),