2020-07-15 11:19:18 +02:00
|
|
|
#include "extract.h"
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
#define LOG_MODULE "extract"
|
|
|
|
|
#define LOG_ENABLE_DBG 1
|
|
|
|
|
#include "log.h"
|
|
|
|
|
|
|
|
|
|
struct extraction_context {
|
|
|
|
|
wchar_t *buf;
|
|
|
|
|
size_t size;
|
|
|
|
|
size_t idx;
|
|
|
|
|
size_t empty_count;
|
2020-08-20 19:21:25 +02:00
|
|
|
size_t newline_count;
|
2020-07-15 11:32:40 +02:00
|
|
|
bool failed;
|
2020-07-15 11:19:18 +02:00
|
|
|
const struct row *last_row;
|
|
|
|
|
const struct cell *last_cell;
|
|
|
|
|
enum selection_kind selection_kind;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct extraction_context *
|
|
|
|
|
extract_begin(enum selection_kind kind)
|
|
|
|
|
{
|
|
|
|
|
struct extraction_context *ctx = malloc(sizeof(*ctx));
|
2020-08-08 20:34:30 +01:00
|
|
|
if (unlikely(ctx == NULL)) {
|
|
|
|
|
LOG_ERRNO("malloc() failed");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-15 11:19:18 +02:00
|
|
|
*ctx = (struct extraction_context){
|
|
|
|
|
.selection_kind = kind,
|
|
|
|
|
};
|
|
|
|
|
return ctx;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-16 17:46:02 +02:00
|
|
|
static bool
|
|
|
|
|
ensure_size(struct extraction_context *ctx, size_t additional_chars)
|
|
|
|
|
{
|
|
|
|
|
while (ctx->size < ctx->idx + additional_chars) {
|
|
|
|
|
size_t new_size = ctx->size == 0 ? 512 : ctx->size * 2;
|
|
|
|
|
wchar_t *new_buf = realloc(ctx->buf, new_size * sizeof(wchar_t));
|
|
|
|
|
|
|
|
|
|
if (new_buf == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
ctx->buf = new_buf;
|
|
|
|
|
ctx->size = new_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(ctx->size >= ctx->idx + additional_chars);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2020-07-31 17:09:06 +02:00
|
|
|
|
2020-07-15 11:19:18 +02:00
|
|
|
bool
|
|
|
|
|
extract_finish(struct extraction_context *ctx, char **text, size_t *len)
|
|
|
|
|
{
|
2020-07-15 11:32:40 +02:00
|
|
|
bool ret = false;
|
|
|
|
|
|
2020-07-15 11:19:18 +02:00
|
|
|
if (text == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
|
2020-07-15 11:32:40 +02:00
|
|
|
*text = NULL;
|
|
|
|
|
if (len != NULL)
|
|
|
|
|
*len = 0;
|
|
|
|
|
|
|
|
|
|
if (ctx->failed)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2020-07-15 11:19:18 +02:00
|
|
|
if (ctx->idx == 0) {
|
|
|
|
|
/* Selection of empty cells only */
|
2020-07-16 17:46:02 +02:00
|
|
|
if (!ensure_size(ctx, 1))
|
|
|
|
|
goto out;
|
2021-01-03 16:21:36 +01:00
|
|
|
ctx->buf[ctx->idx++] = L'\0';
|
2020-07-15 11:19:18 +02:00
|
|
|
} else {
|
|
|
|
|
assert(ctx->idx > 0);
|
2021-01-03 16:18:42 +01:00
|
|
|
assert(ctx->idx <= ctx->size);
|
2020-07-15 11:19:18 +02:00
|
|
|
if (ctx->buf[ctx->idx - 1] == L'\n')
|
|
|
|
|
ctx->buf[ctx->idx - 1] = L'\0';
|
2021-01-03 16:18:42 +01:00
|
|
|
else {
|
|
|
|
|
if (!ensure_size(ctx, 1))
|
|
|
|
|
goto out;
|
2021-01-03 16:21:36 +01:00
|
|
|
ctx->buf[ctx->idx++] = L'\0';
|
2021-01-03 16:18:42 +01:00
|
|
|
}
|
2020-07-15 11:19:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t _len = wcstombs(NULL, ctx->buf, 0);
|
|
|
|
|
if (_len == (size_t)-1) {
|
|
|
|
|
LOG_ERRNO("failed to convert selection to UTF-8");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*text = malloc(_len + 1);
|
2020-08-08 20:34:30 +01:00
|
|
|
if (unlikely(text == NULL)) {
|
|
|
|
|
LOG_ERRNO("malloc() failed");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-15 11:19:18 +02:00
|
|
|
wcstombs(*text, ctx->buf, _len + 1);
|
|
|
|
|
|
|
|
|
|
if (len != NULL)
|
|
|
|
|
*len = _len;
|
|
|
|
|
|
|
|
|
|
ret = true;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
free(ctx->buf);
|
|
|
|
|
free(ctx);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2020-07-15 11:31:38 +02:00
|
|
|
extract_one(const struct terminal *term, const struct row *row,
|
|
|
|
|
const struct cell *cell, int col, void *context)
|
2020-07-15 11:19:18 +02:00
|
|
|
{
|
|
|
|
|
struct extraction_context *ctx = context;
|
|
|
|
|
|
|
|
|
|
if (cell->wc == CELL_MULT_COL_SPACER)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (ctx->last_row != NULL && row != ctx->last_row) {
|
|
|
|
|
/* New row - determine if we should insert a newline or not */
|
|
|
|
|
|
|
|
|
|
if (ctx->selection_kind == SELECTION_NONE ||
|
|
|
|
|
ctx->selection_kind == SELECTION_NORMAL)
|
|
|
|
|
{
|
|
|
|
|
if (ctx->last_row->linebreak ||
|
|
|
|
|
ctx->empty_count > 0 ||
|
|
|
|
|
cell->wc == 0)
|
|
|
|
|
{
|
|
|
|
|
/* Row has a hard linebreak, or either last cell or
|
|
|
|
|
* current cell is empty */
|
|
|
|
|
|
2020-08-20 19:21:25 +02:00
|
|
|
/* Don't emit newline just yet - only if there are
|
|
|
|
|
* non-empty cells following it */
|
|
|
|
|
ctx->newline_count++;
|
2020-07-15 11:19:18 +02:00
|
|
|
ctx->empty_count = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (ctx->selection_kind == SELECTION_BLOCK) {
|
|
|
|
|
/* Always insert a linebreak */
|
|
|
|
|
if (!ensure_size(ctx, 1))
|
2020-07-15 11:32:40 +02:00
|
|
|
goto err;
|
2020-07-15 11:19:18 +02:00
|
|
|
|
|
|
|
|
ctx->buf[ctx->idx++] = L'\n';
|
|
|
|
|
ctx->empty_count = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cell->wc == 0) {
|
|
|
|
|
ctx->empty_count++;
|
|
|
|
|
ctx->last_row = row;
|
|
|
|
|
ctx->last_cell = cell;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-20 19:21:25 +02:00
|
|
|
/* Insert pending newlines, and replace empty cells with spaces */
|
|
|
|
|
if (!ensure_size(ctx, ctx->newline_count + ctx->empty_count))
|
2020-07-15 11:32:40 +02:00
|
|
|
goto err;
|
2020-07-15 11:19:18 +02:00
|
|
|
|
2020-08-20 19:21:25 +02:00
|
|
|
for (size_t i = 0; i < ctx->newline_count; i++)
|
|
|
|
|
ctx->buf[ctx->idx++] = L'\n';
|
|
|
|
|
|
2020-07-15 11:19:18 +02:00
|
|
|
for (size_t i = 0; i < ctx->empty_count; i++)
|
|
|
|
|
ctx->buf[ctx->idx++] = L' ';
|
2020-08-20 19:21:25 +02:00
|
|
|
|
|
|
|
|
ctx->newline_count = 0;
|
2020-07-15 11:19:18 +02:00
|
|
|
ctx->empty_count = 0;
|
|
|
|
|
|
|
|
|
|
if (cell->wc >= CELL_COMB_CHARS_LO &&
|
|
|
|
|
cell->wc < (CELL_COMB_CHARS_LO + term->composed_count))
|
|
|
|
|
{
|
|
|
|
|
const struct composed *composed
|
|
|
|
|
= &term->composed[cell->wc - CELL_COMB_CHARS_LO];
|
|
|
|
|
|
|
|
|
|
if (!ensure_size(ctx, 1 + composed->count))
|
2020-07-15 11:32:40 +02:00
|
|
|
goto err;
|
2020-07-15 11:19:18 +02:00
|
|
|
|
|
|
|
|
ctx->buf[ctx->idx++] = composed->base;
|
|
|
|
|
for (size_t i = 0; i < composed->count; i++)
|
|
|
|
|
ctx->buf[ctx->idx++] = composed->combining[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
if (!ensure_size(ctx, 1))
|
2020-07-15 11:32:40 +02:00
|
|
|
goto err;
|
2020-07-15 11:19:18 +02:00
|
|
|
ctx->buf[ctx->idx++] = cell->wc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx->last_row = row;
|
|
|
|
|
ctx->last_cell = cell;
|
|
|
|
|
return true;
|
2020-07-15 11:32:40 +02:00
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
ctx->failed = true;
|
|
|
|
|
return false;
|
2020-07-15 11:19:18 +02:00
|
|
|
}
|