Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
d9dbee9993 | |||
740445f425 | |||
f849e85e9f | |||
628b131741 | |||
fb3d6c319b | |||
04b8a6dcda |
@ -29,6 +29,7 @@ src = [
|
|||||||
'src/util/process.c',
|
'src/util/process.c',
|
||||||
'src/util/strbuf.c',
|
'src/util/strbuf.c',
|
||||||
'src/util/str_util.c',
|
'src/util/str_util.c',
|
||||||
|
'src/util/term.c',
|
||||||
'src/util/thread.c',
|
'src/util/thread.c',
|
||||||
'src/util/tick.c',
|
'src/util/tick.c',
|
||||||
]
|
]
|
||||||
@ -187,7 +188,9 @@ if get_option('buildtype') == 'debug'
|
|||||||
'tests/test_cli.c',
|
'tests/test_cli.c',
|
||||||
'src/cli.c',
|
'src/cli.c',
|
||||||
'src/options.c',
|
'src/options.c',
|
||||||
|
'src/util/strbuf.c',
|
||||||
'src/util/str_util.c',
|
'src/util/str_util.c',
|
||||||
|
'src/util/term.c',
|
||||||
]],
|
]],
|
||||||
['test_clock', [
|
['test_clock', [
|
||||||
'tests/test_clock.c',
|
'tests/test_clock.c',
|
||||||
@ -196,6 +199,7 @@ if get_option('buildtype') == 'debug'
|
|||||||
['test_control_msg_serialize', [
|
['test_control_msg_serialize', [
|
||||||
'tests/test_control_msg_serialize.c',
|
'tests/test_control_msg_serialize.c',
|
||||||
'src/control_msg.c',
|
'src/control_msg.c',
|
||||||
|
'src/util/strbuf.c',
|
||||||
'src/util/str_util.c',
|
'src/util/str_util.c',
|
||||||
]],
|
]],
|
||||||
['test_device_msg_deserialize', [
|
['test_device_msg_deserialize', [
|
||||||
@ -211,6 +215,7 @@ if get_option('buildtype') == 'debug'
|
|||||||
]],
|
]],
|
||||||
['test_strutil', [
|
['test_strutil', [
|
||||||
'tests/test_strutil.c',
|
'tests/test_strutil.c',
|
||||||
|
'src/util/strbuf.c',
|
||||||
'src/util/str_util.c',
|
'src/util/str_util.c',
|
||||||
]],
|
]],
|
||||||
]
|
]
|
||||||
|
1185
app/src/cli.c
1185
app/src/cli.c
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,11 @@
|
|||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include "util/strbuf.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
@ -209,3 +211,81 @@ utf8_from_wide_char(const wchar_t *ws) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
char *wrap_lines(const char *input, unsigned columns, unsigned indent) {
|
||||||
|
assert(indent < columns);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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 ");
|
||||||
|
size_t new_col = col + sep_idx;
|
||||||
|
if (pending == ' ') {
|
||||||
|
// The pending space counts
|
||||||
|
++new_col;
|
||||||
|
}
|
||||||
|
bool wrap = new_col > 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;
|
||||||
|
}
|
||||||
|
@ -62,4 +62,8 @@ char *
|
|||||||
utf8_from_wide_char(const wchar_t *s);
|
utf8_from_wide_char(const wchar_t *s);
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
51
app/src/util/term.c
Normal file
51
app/src/util/term.c
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#include "term.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include <windows.h>
|
||||||
|
#else
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <sys/ioctl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_term_get_size(unsigned *rows, unsigned *cols) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||||
|
|
||||||
|
bool ok =
|
||||||
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
||||||
|
if (!ok) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rows) {
|
||||||
|
assert(csbi.srWindow.Bottom >= csbi.srWindow.Top);
|
||||||
|
*rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cols) {
|
||||||
|
assert(csbi.srWindow.Right >= csbi.srWindow.Left);
|
||||||
|
*cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
struct winsize ws;
|
||||||
|
int r = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
|
||||||
|
if (r == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rows) {
|
||||||
|
*rows = ws.ws_row;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cols) {
|
||||||
|
*cols = ws.ws_col;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
11
app/src/util/term.h
Normal file
11
app/src/util/term.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef SC_TERM_H
|
||||||
|
#define SC_TERM_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_term_get_size(unsigned *rows, unsigned *cols);
|
||||||
|
|
||||||
|
#endif
|
@ -299,6 +299,44 @@ static void test_strlist_contains(void) {
|
|||||||
assert(strlist_contains("xyz", '\0', "xyz"));
|
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 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 at a space\n"
|
||||||
|
" or a line 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[]) {
|
int main(int argc, char *argv[]) {
|
||||||
(void) argc;
|
(void) argc;
|
||||||
(void) argv;
|
(void) argv;
|
||||||
@ -317,5 +355,6 @@ int main(int argc, char *argv[]) {
|
|||||||
test_parse_integers();
|
test_parse_integers();
|
||||||
test_parse_integer_with_suffix();
|
test_parse_integer_with_suffix();
|
||||||
test_strlist_contains();
|
test_strlist_contains();
|
||||||
|
test_wrap_lines();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user