Compare commits

...

4 Commits

Author SHA1 Message Date
Romain Vimont
e7add09fa9 Generate getopt params from option structures
Use the option descriptions to generate the optstring and longopts
parameters for the getopt_long() command.

That way, the options are completely described in a single place.
2021-11-07 21:38:42 +01:00
Romain Vimont
4a5e16ec97 Structure shortcuts help 2021-11-07 21:38:42 +01:00
Romain Vimont
a7e80913b6 Structure command line options help
It will allow to correctly print the help for the current terminal size
(even if for now it is hardcoded to 80 columns).
2021-11-07 21:38:41 +01:00
Romain Vimont
e537ddb5cc Add line wrapper
Add a tool to wrap lines at words boundaries (spaces) to fit in a
specified number of columns, left-indented by a specified number of
spaces.
2021-11-07 21:35:57 +01:00
5 changed files with 879 additions and 413 deletions

View File

@@ -187,6 +187,7 @@ if get_option('buildtype') == 'debug'
'tests/test_cli.c',
'src/cli.c',
'src/options.c',
'src/util/strbuf.c',
'src/util/str_util.c',
]],
['test_clock', [
@@ -196,6 +197,7 @@ if get_option('buildtype') == 'debug'
['test_control_msg_serialize', [
'tests/test_control_msg_serialize.c',
'src/control_msg.c',
'src/util/strbuf.c',
'src/util/str_util.c',
]],
['test_device_msg_deserialize', [
@@ -211,6 +213,7 @@ if get_option('buildtype') == 'debug'
]],
['test_strutil', [
'tests/test_strutil.c',
'src/util/strbuf.c',
'src/util/str_util.c',
]],
]

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,11 @@
#include "str_util.h"
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "util/strbuf.h"
#ifdef _WIN32
# include <windows.h>
@@ -209,3 +211,76 @@ utf8_from_wide_char(const wchar_t *ws) {
}
#endif
char *wrap_lines(const char *input, unsigned columns, unsigned indent) {
struct sc_strbuf buf;
// The output string should not be a lot longer than the input string (just
// a few '\n' added), so this initial capacity should almost always avoid
// internal realloc() in string buffer
size_t cap = strlen(input) * 3 / 2;
if (!sc_strbuf_init(&buf, cap)) {
return false;
}
assert(indent < columns);
#define APPEND(S,N) if (!sc_strbuf_append(&buf, S, N)) goto error
#define APPEND_CHAR(C) if (!sc_strbuf_append_char(&buf, C)) goto error
#define APPEND_N(C,N) if (!sc_strbuf_append_n(&buf, C, N)) goto error
#define APPEND_INDENT() if (indent) APPEND_N(' ', indent)
APPEND_INDENT();
// The last separator encountered, it must be inserted only conditionnaly,
// depending on the next token
char pending = 0;
// col tracks the current column in the current line
size_t col = indent;
while (*input) {
size_t sep_idx = strcspn(input, "\n ");
bool wrap = col + sep_idx > columns;
char sep = input[sep_idx];
if (sep == ' ')
sep = ' ';
if (wrap) {
APPEND_CHAR('\n');
APPEND_INDENT();
col = indent;
} else if (pending) {
APPEND_CHAR(pending);
++col;
if (pending == '\n')
{
APPEND_INDENT();
col = indent;
}
}
if (sep_idx) {
APPEND(input, sep_idx);
col += sep_idx;
}
pending = sep;
input += sep_idx;
if (*input != '\0') {
// Skip the separator
++input;
}
}
if (pending)
APPEND_CHAR(pending);
return buf.s;
error:
free(buf.s);
return NULL;
}

View File

@@ -62,4 +62,8 @@ char *
utf8_from_wide_char(const wchar_t *s);
#endif
// Wrap input lines at words boundaries (spaces) so that they fit in 'columns'
// columns, left-indented by 'indent' spaces
char *wrap_lines(const char *input, unsigned columns, unsigned indent);
#endif

View File

@@ -299,6 +299,45 @@ static void test_strlist_contains(void) {
assert(strlist_contains("xyz", '\0', "xyz"));
}
static void test_wrap_lines(void) {
const char *s = "This is a text to test line wrapping. The lines must be "
"wrapped either at a space or a line break.\n"
"\n"
"This rectangle must remains a rectangle because it is "
"drawn in lines having lengths lower than the specified "
"number of columns:\n"
" +----+\n"
" | |\n"
" +----+\n";
// |---- 1 1 2 2|
// |0 5 0 5 0 3| <-- 24 columns
const char *expected = " This is a text to\n"
" test line wrapping.\n"
" The lines must be\n"
" wrapped either at a\n"
" space or a line\n"
" break.\n"
" \n"
" This rectangle must\n"
" remains a rectangle\n"
" because it is drawn\n"
" in lines having\n"
" lengths lower than\n"
" the specified number\n"
" of columns:\n"
" +----+\n"
" | |\n"
" +----+\n";
char *formatted = wrap_lines(s, 24, 4);
assert(formatted);
assert(!strcmp(formatted, expected));
free(formatted);
}
int main(int argc, char *argv[]) {
(void) argc;
(void) argv;
@@ -317,5 +356,6 @@ int main(int argc, char *argv[]) {
test_parse_integers();
test_parse_integer_with_suffix();
test_strlist_contains();
test_wrap_lines();
return 0;
}