mirror of
https://github.com/ascii-boxes/boxes.git
synced 2025-01-05 21:49:04 +01:00
Rewrite 'remove' module to enable unicode, double-with chars, and coloring
This commit is contained in:
parent
9511d18bc1
commit
4175d72bc8
@ -121,7 +121,7 @@ parser.o: parser.c boxes.h bxstring.h lex.yy.h parsecode.h parser.h parsing.h
|
||||
parsing.o: parsing.c parsing.h bxstring.h parser.h lex.yy.h boxes.h tools.h config.h | check_dir
|
||||
query.o: query.c query.h boxes.h list.h tools.h config.h | check_dir
|
||||
regulex.o: regulex.c regulex.h boxes.h tools.h unicode.h config.h | check_dir
|
||||
remove.o: remove.c remove.h boxes.h shape.h tools.h unicode.h config.h | check_dir
|
||||
remove.o: remove.c remove.h boxes.h detect.h shape.h tools.h unicode.h config.h | check_dir
|
||||
shape.o: shape.c shape.h boxes.h bxstring.h tools.h config.h | check_dir
|
||||
tools.o: tools.c tools.h boxes.h regulex.h shape.h unicode.h config.h | check_dir
|
||||
unicode.o: unicode.c unicode.h boxes.h tools.h config.h | check_dir
|
||||
|
@ -51,7 +51,8 @@ typedef struct {
|
||||
/** Number of invisible characters in the original string */
|
||||
size_t num_chars_invisible;
|
||||
|
||||
/** Number of trailing spaces in the original string */
|
||||
/** Number of trailing spaces in the original string. In a line consisting only of spaces, this is zero, because
|
||||
* the spaces would count towards the indent. */
|
||||
size_t trailing;
|
||||
|
||||
/** Array of index values into `memory` of the first actual character (possibly invisible) of each visible
|
||||
|
33
src/detect.c
33
src/detect.c
@ -91,6 +91,7 @@ int design_is_mono(design_t *design)
|
||||
int comp_type_is_viable(comparison_t comp_type, int mono_input, int mono_design)
|
||||
{
|
||||
int result = 1;
|
||||
/* TODO What if a mono box was drawn around colored text? -> literal would be fine. Be less strict? */
|
||||
if ((comp_type == literal && mono_input != mono_design)
|
||||
|| (comp_type == ignore_invisible_input && (mono_input || !mono_design))
|
||||
|| (comp_type == ignore_invisible_shape && (!mono_input || mono_design))
|
||||
@ -171,7 +172,8 @@ uint32_t *prepare_comp_shape(
|
||||
|
||||
|
||||
|
||||
uint32_t *prepare_comp_input(size_t input_line_idx, int trim_left, comparison_t comp_type, size_t offset_right)
|
||||
uint32_t *prepare_comp_input(size_t input_line_idx, int trim_left, comparison_t comp_type, size_t offset_right,
|
||||
size_t *out_indent, size_t *out_trailing)
|
||||
{
|
||||
if (input_line_idx >= input.num_lines) {
|
||||
bx_fprintf(stderr, "%s: prepare_comp_input(%d, %d, %s, %d): Index out of bounds\n", PROJECT,
|
||||
@ -187,11 +189,20 @@ uint32_t *prepare_comp_input(size_t input_line_idx, int trim_left, comparison_t
|
||||
uint32_t *p = visible + input_line->num_chars_visible - input_line->trailing - offset_right;
|
||||
if (p >= visible) {
|
||||
result = p;
|
||||
if (out_indent != NULL) {
|
||||
*out_indent = BMAX(input_line->indent - (size_t) (p - visible), (size_t) 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_t ltrim = trim_left ? input_line->indent : 0;
|
||||
result = get_visible_text(input.lines + input_line_idx) + ltrim;
|
||||
if (out_indent != NULL) {
|
||||
*out_indent = trim_left ? 0 : input_line->indent;
|
||||
}
|
||||
}
|
||||
if (out_trailing != NULL) {
|
||||
*out_trailing = input_line->trailing;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -200,10 +211,20 @@ uint32_t *prepare_comp_input(size_t input_line_idx, int trim_left, comparison_t
|
||||
- offset_right;
|
||||
if (idx >= 0) {
|
||||
result = input_line->memory + idx;
|
||||
if (out_indent != NULL) {
|
||||
*out_indent = BMAX(input_line->first_char[input_line->indent] - (size_t) idx, (size_t) 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = trim_left ? bxs_unindent_ptr(input_line) : input_line->memory;
|
||||
if (out_indent != NULL) {
|
||||
*out_indent = trim_left ? 0 : input_line->first_char[input_line->indent];
|
||||
}
|
||||
}
|
||||
if (out_trailing != NULL) {
|
||||
*out_trailing = input_line->num_chars
|
||||
- input_line->first_char[input_line->num_chars_visible - input_line->trailing];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -245,7 +266,7 @@ static size_t find_west_corner(design_t *current_design, comparison_t comp_type,
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t *input_relevant = prepare_comp_input(a, 1, comp_type, 0);
|
||||
uint32_t *input_relevant = prepare_comp_input(a, 1, comp_type, 0, NULL, NULL);
|
||||
if (u32_strncmp(input_relevant, shape_relevant, length_relevant) == 0) {
|
||||
++hits; /* CHECK more hit points for longer matches, or simple boxes might match too easily */
|
||||
}
|
||||
@ -295,7 +316,7 @@ static size_t find_east_corner(design_t *current_design, comparison_t comp_type,
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t *input_relevant = prepare_comp_input(a, 0, comp_type, length_relevant);
|
||||
uint32_t *input_relevant = prepare_comp_input(a, 0, comp_type, length_relevant, NULL, NULL);
|
||||
if (u32_strncmp(input_relevant, shape_relevant, length_relevant) == 0) {
|
||||
++hits; /* CHECK more hit points for longer matches, or simple boxes might match too easily */
|
||||
}
|
||||
@ -345,8 +366,8 @@ static size_t find_horizontal_shape(design_t *current_design, comparison_t comp_
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t *input_relevant = prepare_comp_input(a, 1, comp_type, 0); /* CHECK this eats blank NW corners */
|
||||
|
||||
uint32_t *input_relevant = prepare_comp_input(a, 1, comp_type, 0, NULL, NULL);
|
||||
/* CHECK this eats blank NW corners */
|
||||
uint32_t *p = u32_strstr(input_relevant, shape_relevant);
|
||||
if (p) {
|
||||
if (current_design->shape[hshape].elastic) {
|
||||
@ -397,7 +418,7 @@ static size_t find_vertical_west(design_t *current_design, comparison_t comp_typ
|
||||
for (size_t k = empty[BTOP] ? 0 : current_design->shape[NW].height;
|
||||
k < input.num_lines - (empty[BBOT] ? 0 : current_design->shape[SW].height); ++k)
|
||||
{
|
||||
uint32_t *input_relevant = prepare_comp_input(k, 1, comp_type, 0);
|
||||
uint32_t *input_relevant = prepare_comp_input(k, 1, comp_type, 0, NULL, NULL);
|
||||
|
||||
for (size_t j = 0; j < current_design->shape[vshape].height; ++j) {
|
||||
bxstr_t *shape_line = current_design->shape[vshape].mbcs[j];
|
||||
|
11
src/detect.h
11
src/detect.h
@ -41,7 +41,7 @@ typedef enum {
|
||||
} comparison_t;
|
||||
|
||||
/** The elements of `comparision_t` as strings for printing */
|
||||
char *comparison_name[];
|
||||
extern char *comparison_name[];
|
||||
|
||||
|
||||
/**
|
||||
@ -94,10 +94,17 @@ uint32_t *prepare_comp_shape(
|
||||
* @param offset_right `> 0`: the result pointer will point to the last `offset_right` non-blank characters (visible
|
||||
* or invisible) of the string; `== 0`: result pointer will point to start of string (possibly trimmed as
|
||||
* per `trim_left`)
|
||||
* @param out_indent pointer to a memory location where it is stored how many characters at the start of the result
|
||||
* string are considered part of the indent, for example leading spaces, including any invisible characters.
|
||||
* If NULL, it is ignored.
|
||||
* @param out_trailing pointer to a memory location where it is stored how many characters at the end of the result
|
||||
* string are considered trailing, for example trailing spaces, including any invisible characters. If NULL,
|
||||
* it is ignored.
|
||||
* @return the relevant part of the selected input line, in original memory (do not free this), or
|
||||
* NULL if `input_line_idx` out of bounds or `offset_right` too large
|
||||
*/
|
||||
uint32_t *prepare_comp_input(size_t input_line_idx, int trim_left, comparison_t comp_type, size_t offset_right);
|
||||
uint32_t *prepare_comp_input(size_t input_line_idx, int trim_left, comparison_t comp_type, size_t offset_right,
|
||||
size_t *out_indent, size_t *out_trailing);
|
||||
|
||||
|
||||
/**
|
||||
|
1343
src/remove.c
1343
src/remove.c
File diff suppressed because it is too large
Load Diff
12
src/remove.h
12
src/remove.h
@ -21,7 +21,19 @@
|
||||
#define REMOVE_H
|
||||
|
||||
|
||||
/**
|
||||
* Remove box from input.
|
||||
* @return == 0: success;
|
||||
* \!= 0: error
|
||||
*/
|
||||
int remove_box();
|
||||
|
||||
|
||||
/**
|
||||
* Output contents of input line list "as is" to standard output, except for removal of trailing spaces (trimming).
|
||||
* The trimming is performed on the actual input lines, modifying them.
|
||||
* @param trim_only only perform the trimming of trailing spaces, but do not output anything
|
||||
*/
|
||||
void output_input(const int trim_only);
|
||||
|
||||
|
||||
|
795
src/remove.old.c
Normal file
795
src/remove.old.c
Normal file
@ -0,0 +1,795 @@
|
||||
/*
|
||||
* boxes - Command line filter to draw/remove ASCII boxes around text
|
||||
* Copyright (c) 1999-2023 Thomas Jensen and the boxes contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License, version 3, as published by the Free Software Foundation.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
* You should have received a copy of the GNU General Public License along with this program.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*/
|
||||
|
||||
/*
|
||||
* Box removal, i.e. the deletion of boxes
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistr.h>
|
||||
|
||||
#include "detect.h"
|
||||
#include "shape.h"
|
||||
#include "boxes.h"
|
||||
#include "tools.h"
|
||||
#include "unicode.h"
|
||||
#include "remove.h"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Find positions of west and east box parts in line.
|
||||
* @param line line to examine
|
||||
* @param ws result parameter: west start as pointer to a character in `line->text->memory`
|
||||
* @param we result parameter: west end as pointer to a character in `line->text->memory`
|
||||
* @param es result parameter: east start as pointer to a character in `line->text->memory`
|
||||
* @param ee result parameter: east end as pointer to a character in `line->text->memory`
|
||||
* @return > 0: a match was found (ws etc. are set to indicate positions);
|
||||
* == 0: no match was found;
|
||||
* < 0: internal error (out of memory)
|
||||
*/
|
||||
static int best_match(const line_t *line, uint32_t **ws, uint32_t **we, uint32_t **es, uint32_t **ee)
|
||||
{
|
||||
size_t numw = 0; /* number of shape lines on west side */
|
||||
size_t nume = 0; /* number of shape lines on east side */
|
||||
size_t j; /* counts number of lines of all shapes tested */
|
||||
size_t k; /* line counter within shape */
|
||||
int w; /* shape counter */
|
||||
sentry_t *cs; /* current shape */
|
||||
uint32_t *s; /* duplicate of current shape part */
|
||||
uint32_t *p; /* position found by u32_strstr() */
|
||||
size_t cq; /* current quality */
|
||||
uint32_t *q; /* space check rover */
|
||||
size_t quality;
|
||||
|
||||
*ws = *we = *es = *ee = NULL;
|
||||
|
||||
numw = opt.design->shape[WNW].height;
|
||||
numw += opt.design->shape[W].height;
|
||||
numw += opt.design->shape[WSW].height;
|
||||
|
||||
nume = opt.design->shape[ENE].height;
|
||||
nume += opt.design->shape[E].height;
|
||||
nume += opt.design->shape[ESE].height;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf (stderr, "Number of WEST side shape lines: %d\n", (int) numw);
|
||||
fprintf (stderr, "Number of EAST side shape lines: %d\n", (int) nume);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Find match for WEST side
|
||||
*/
|
||||
if (!empty_side(opt.design->shape, BLEF)) {
|
||||
quality = 0;
|
||||
cs = opt.design->shape + WNW;
|
||||
for (j = 0, k = 0, w = 3; j < numw; ++j, ++k) {
|
||||
if (k == cs->height) {
|
||||
k = 0;
|
||||
cs = opt.design->shape + west_side[--w];
|
||||
}
|
||||
|
||||
if (bxs_is_blank(cs->mbcs[k]) && !(quality == 0 && j == numw - 1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
s = u32_strdup(cs->mbcs[k]->memory);
|
||||
if (s == NULL) {
|
||||
perror(PROJECT);
|
||||
return -1;
|
||||
}
|
||||
cq = cs->width;
|
||||
|
||||
do {
|
||||
p = u32_strstr(line->text->memory, s);
|
||||
if (p) {
|
||||
q = p - 1;
|
||||
while (q >= line->text->memory) {
|
||||
if (*q-- != ' ') {
|
||||
p = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (p) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!p && cq) {
|
||||
if (*s == char_space) {
|
||||
u32_move(s, s + 1, cq--);
|
||||
} else if (s[cq - 1] == char_space) {
|
||||
s[--cq] = char_nul;
|
||||
} else {
|
||||
cq = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (cq && !p);
|
||||
|
||||
if (cq == 0) {
|
||||
BFREE (s);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the current match is the best yet, adjust result values
|
||||
*/
|
||||
if (cq > quality) {
|
||||
quality = cq;
|
||||
*ws = p;
|
||||
*we = p + cq;
|
||||
}
|
||||
|
||||
BFREE (s);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find match for EAST side
|
||||
*/
|
||||
if (!empty_side(opt.design->shape, BRIG)) {
|
||||
quality = 0;
|
||||
cs = opt.design->shape + ENE;
|
||||
for (j = 0, k = 0, w = 1; j < nume; ++j, ++k) {
|
||||
if (k == cs->height) {
|
||||
k = 0;
|
||||
cs = opt.design->shape + east_side[++w];
|
||||
}
|
||||
#ifdef DEBUG
|
||||
char *mbcs_temp = bxs_to_output(cs->mbcs[k]);
|
||||
fprintf(stderr, "\nj %d, k %d, w %d, cs->chars[k] = \"%s\", cs->mbcs[k] = \"%s\"\n",
|
||||
(int) j, (int) k, w, cs->chars[k] ? cs->chars[k] : "(null)", mbcs_temp);
|
||||
BFREE(mbcs_temp);
|
||||
#endif
|
||||
|
||||
if (bxs_is_blank(cs->mbcs[k])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
s = u32_strdup(cs->mbcs[k]->memory);
|
||||
if (s == NULL) {
|
||||
perror(PROJECT);
|
||||
return -1;
|
||||
}
|
||||
cq = cs->width;
|
||||
|
||||
do {
|
||||
p = u32_strnrstr(line->text->memory, s, cq, 0);
|
||||
if (p) {
|
||||
q = p + cq;
|
||||
while (*q) {
|
||||
if (*q++ != ' ') {
|
||||
p = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (p) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!p && cq) {
|
||||
if (*s == ' ') {
|
||||
u32_move(s, s + 1, cq--);
|
||||
} else if (s[cq - 1] == ' ') {
|
||||
s[--cq] = '\0';
|
||||
} else {
|
||||
cq = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (cq && !p);
|
||||
|
||||
if (cq == 0) {
|
||||
BFREE (s);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the current match is the best yet, adjust result values
|
||||
*/
|
||||
if (cq > quality) {
|
||||
quality = cq;
|
||||
*es = p;
|
||||
*ee = p + cq;
|
||||
}
|
||||
|
||||
BFREE (s);
|
||||
}
|
||||
}
|
||||
|
||||
return *ws || *es ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int hmm(const int aside, const size_t follow,
|
||||
const char *p, const char *ecs, const int cnt)
|
||||
/*
|
||||
* (horizontal middle match)
|
||||
*
|
||||
* aside box part to check (BTOP or BBOT)
|
||||
* follow index of line number in shape spec to check
|
||||
* p current check position
|
||||
* ecs pointer to first char of east corner shape
|
||||
* cnt current shape to check (0 == leftmost middle shape)
|
||||
*
|
||||
* Recursive helper function for detect_horiz(), uses backtracking
|
||||
*
|
||||
* RETURNS: == 0 success
|
||||
* != 0 error
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*/
|
||||
{
|
||||
int cmp;
|
||||
sentry_t *cs;
|
||||
shape_t sh;
|
||||
int rc;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "hmm (%s, %d, \'%c\', \'%c\', %d)\n",
|
||||
aside == BTOP ? "BTOP" : "BBOT", (int) follow, p[0], *ecs, cnt);
|
||||
#endif
|
||||
|
||||
if (p > ecs) { /* last shape tried was too long */
|
||||
return 2;
|
||||
}
|
||||
|
||||
sh = leftmost(aside, cnt);
|
||||
if (sh == NUM_SHAPES) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
cs = opt.design->shape + sh;
|
||||
|
||||
cmp = strncmp(p, cs->chars[follow], cs->width);
|
||||
|
||||
if (cmp == 0) {
|
||||
if (p + cs->width == ecs) {
|
||||
if (leftmost(aside, cnt + 1) == NUM_SHAPES) {
|
||||
return 0; /* good! all clear, it matched */
|
||||
} else {
|
||||
return 3;
|
||||
} /* didn't use all shapes to do it */
|
||||
}
|
||||
if (cs->elastic) {
|
||||
rc = hmm(aside, follow, p + cs->width, ecs, cnt);
|
||||
#ifdef DEBUG
|
||||
fprintf (stderr, "hmm returned %d\n", rc);
|
||||
#endif
|
||||
if (rc) {
|
||||
rc = hmm(aside, follow, p + cs->width, ecs, cnt + 1);
|
||||
#ifdef DEBUG
|
||||
fprintf (stderr, "hmm returned %d\n", rc);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
rc = hmm(aside, follow, p + cs->width, ecs, cnt + 1);
|
||||
#ifdef DEBUG
|
||||
fprintf (stderr, "hmm returned %d\n", rc);
|
||||
#endif
|
||||
}
|
||||
if (rc == 0) {
|
||||
return 0; /* we're on the way back */
|
||||
} else {
|
||||
return 4;
|
||||
} /* can't continue on this path */
|
||||
}
|
||||
else {
|
||||
return 5; /* no match */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int detect_horiz(const int aside, size_t *hstart, size_t *hend)
|
||||
/*
|
||||
* Detect which part of the input belongs to the top/bottom of the box
|
||||
*
|
||||
* aside part of box to detect (BTOP or BBOT)
|
||||
* hstart index of first line of detected box part (result)
|
||||
* hend index of first line following detected box part (result)
|
||||
*
|
||||
* We assume the horizontal parts of the box to be in one piece, i.e. no
|
||||
* blank lines inserted. Lines may be missing, though. Lines may not be
|
||||
* duplicated. They may be shifted left and right by inserting whitespace,
|
||||
* but whitespace which is part of the box must not have been deleted
|
||||
* (unless it's because an entire box side is empty). Box part lines may
|
||||
* even differ in length as long as each line is in itself a valid
|
||||
* horizontal box line.
|
||||
*
|
||||
* RETURNS: == 0 success (hstart & hend are set)
|
||||
* != 0 error
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*/
|
||||
{
|
||||
size_t follow; /* possible box line */
|
||||
sentry_t *cs; /* current shape */
|
||||
line_t *line; /* currently processed input line */
|
||||
size_t lcnt; /* line counter */
|
||||
char *p = NULL; /* middle line part scanner */
|
||||
char *q; /* space check rover */
|
||||
char *wcs = NULL; /* west corner shape position */
|
||||
char *ecs = NULL; /* east corner shape position */
|
||||
int mmok = 0; /* true if middle match was ok */
|
||||
size_t mheight; /* regular height of box part */
|
||||
int result_init = 0; /* true if hstart etc. was init. */
|
||||
int nowside; /* true if west side is empty */
|
||||
int goeast; /* no. of finds to ignore on right */
|
||||
int gowest; /* set to request search start incr. */
|
||||
|
||||
*hstart = *hend = 0;
|
||||
nowside = empty_side(opt.design->shape, BLEF);
|
||||
|
||||
mheight = opt.design->shape[sides[aside][0]].height;
|
||||
if (aside == BTOP) {
|
||||
follow = 0;
|
||||
line = input.lines;
|
||||
}
|
||||
else {
|
||||
follow = mheight - 1;
|
||||
line = input.lines + input.num_lines - 1;
|
||||
}
|
||||
|
||||
for (lcnt = 0; lcnt < mheight && lcnt < input.num_lines
|
||||
&& line >= input.lines; ++lcnt) {
|
||||
goeast = gowest = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "----- Processing line index %2d -----------------------------------------------\n",
|
||||
(int) (aside == BTOP ? lcnt : input.num_lines - lcnt - 1));
|
||||
#endif
|
||||
|
||||
do {
|
||||
/*
|
||||
* Look for west corner shape
|
||||
*/
|
||||
if (!goeast) {
|
||||
if (nowside) {
|
||||
wcs = NULL;
|
||||
if (gowest) {
|
||||
gowest = 0;
|
||||
if (*p == ' ' || *p == '\t') {
|
||||
++p;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
p = line->text->memory;
|
||||
}
|
||||
}
|
||||
else {
|
||||
cs = opt.design->shape + sides[aside][aside == BTOP ? 0 : SHAPES_PER_SIDE - 1];
|
||||
if (gowest) {
|
||||
gowest = 0;
|
||||
wcs = strstr(wcs + 1, cs->chars[follow]);
|
||||
}
|
||||
else {
|
||||
wcs = strstr(line->text, cs->chars[follow]);
|
||||
}
|
||||
if (wcs) {
|
||||
for (q = wcs - 1; q >= line->text; --q) {
|
||||
if (*q != ' ' && *q != '\t') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (q >= line->text) {
|
||||
wcs = NULL;
|
||||
}
|
||||
}
|
||||
if (!wcs) {
|
||||
break;
|
||||
}
|
||||
|
||||
p = wcs + cs->width;
|
||||
}
|
||||
}
|
||||
/* Now, wcs is either NULL (if west side is empty) */
|
||||
/* or not NULL (if west side is not empty). In any case, p */
|
||||
/* points to where we start searching for the east corner. */
|
||||
#ifdef DEBUG
|
||||
if (wcs) {
|
||||
fprintf(stderr, "West corner shape matched at position %d.\n", (int) (wcs - line->text));
|
||||
} else {
|
||||
fprintf(stderr, "West box side is empty.\n");
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Look for east corner shape
|
||||
*/
|
||||
cs = opt.design->shape + sides[aside][aside == BTOP ? SHAPES_PER_SIDE - 1 : 0];
|
||||
ecs = my_strnrstr(p, cs->chars[follow], cs->width, goeast);
|
||||
if (ecs) {
|
||||
for (q = ecs + cs->width; *q; ++q) {
|
||||
if (*q != ' ' && *q != '\t') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*q) {
|
||||
ecs = NULL;
|
||||
}
|
||||
}
|
||||
if (!ecs) {
|
||||
if (goeast == 0) {
|
||||
break;
|
||||
} else {
|
||||
goeast = 0;
|
||||
gowest = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "East corner shape matched at position %d.\n", (int) (ecs - line->text));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check if text between corner shapes is valid
|
||||
*/
|
||||
mmok = !hmm(aside, follow, p, ecs, 0);
|
||||
if (!mmok) {
|
||||
++goeast;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Text between corner shapes is %s.\n", mmok ? "VALID" : "NOT valid");
|
||||
#endif
|
||||
} while (!mmok);
|
||||
|
||||
/*
|
||||
* Proceed to next line
|
||||
*/
|
||||
if (mmok) { /* match found */
|
||||
if (!result_init) {
|
||||
result_init = 1;
|
||||
if (aside == BTOP) {
|
||||
*hstart = lcnt;
|
||||
} else {
|
||||
*hend = (input.num_lines - lcnt - 1) + 1;
|
||||
}
|
||||
}
|
||||
if (aside == BTOP) {
|
||||
*hend = lcnt + 1;
|
||||
} else {
|
||||
*hstart = input.num_lines - lcnt - 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (result_init) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
wcs = NULL;
|
||||
ecs = NULL;
|
||||
p = NULL;
|
||||
mmok = 0;
|
||||
|
||||
if (aside == BTOP) {
|
||||
++follow;
|
||||
++line;
|
||||
}
|
||||
else {
|
||||
--follow;
|
||||
--line;
|
||||
}
|
||||
}
|
||||
|
||||
return result_init ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void add_spaces_to_line(line_t* line, const size_t n)
|
||||
{
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
bxs_append_spaces(line->text, n);
|
||||
analyze_line_ascii(&input, line);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int remove_box()
|
||||
{
|
||||
size_t textstart = 0; /* index of 1st line of box body */
|
||||
size_t textend = 0; /* index of 1st line of south side */
|
||||
size_t boxstart = 0; /* index of 1st line of box */
|
||||
size_t boxend = 0; /* index of 1st line trailing the box */
|
||||
int m; /* true if a match was found */
|
||||
size_t j; /* loop counter */
|
||||
int did_something = 0; /* true if there was something to remove */
|
||||
|
||||
/*
|
||||
* If the user didn't specify a design to remove, autodetect it.
|
||||
* Since this requires knowledge of all available designs, the entire
|
||||
* config file had to be parsed (earlier).
|
||||
*/
|
||||
if (opt.design_choice_by_user == 0) {
|
||||
design_t *tmp = autodetect_design();
|
||||
if (tmp) {
|
||||
opt.design = tmp;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Design autodetection: Removing box of design \"%s\".\n", opt.design->name);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "%s: Box design autodetection failed. Use -d option.\n", PROJECT);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make all lines the same length by adding trailing spaces (needed
|
||||
* for recognition).
|
||||
* Also append a number of spaces to ALL input lines. A greater number
|
||||
* takes more space and time, but enables the correct removal of boxes
|
||||
* whose east sides consist of lots of spaces (the given value). So we
|
||||
* add a number of spaces equal to the east side width.
|
||||
*/
|
||||
const size_t normalized_len = input.maxline + opt.design->shape[NE].width;
|
||||
for (j = 0; j < input.num_lines; ++j) {
|
||||
add_spaces_to_line(input.lines + j, normalized_len - input.lines[j].text->num_columns);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Normalized all lines to %d columns (maxline + east width).\n", (int) input.maxline);
|
||||
print_input_lines(" (remove_box)");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Phase 1: Try to find out how many lines belong to the top of the box
|
||||
*/
|
||||
boxstart = 0;
|
||||
textstart = 0;
|
||||
if (empty_side(opt.design->shape, BTOP)) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "----> Top box side is empty: boxstart == textstart == 0.\n");
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
detect_horiz(BTOP, &boxstart, &textstart);
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "----> First line of box is %d, ", (int) boxstart);
|
||||
fprintf(stderr, "first line of box body (text) is %d.\n", (int) textstart);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Phase 2: Find out how many lines belong to the bottom of the box
|
||||
*/
|
||||
if (empty_side(opt.design->shape, BBOT)) {
|
||||
textend = input.num_lines;
|
||||
boxend = input.num_lines;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "----> Bottom box side is empty: boxend == textend == %d.\n", (int) input.num_lines);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
textend = 0;
|
||||
boxend = 0;
|
||||
detect_horiz(BBOT, &textend, &boxend);
|
||||
if (textend == 0 && boxend == 0) {
|
||||
textend = input.num_lines;
|
||||
boxend = input.num_lines;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "----> Last line of box body (text) is %d, ", (int) (textend - 1));
|
||||
fprintf(stderr, "last line of box is %d.\n", (int) (boxend - 1));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Phase 3: Iterate over body lines, removing box sides where applicable
|
||||
*/
|
||||
for (j = textstart; j < textend; ++j) {
|
||||
uint32_t *ws, *we, *es, *ee; /* west start & end, east start & end */
|
||||
uint32_t *p;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Calling best_match() for line %d:\n", (int) j);
|
||||
#endif
|
||||
m = best_match(input.lines + j, &ws, &we, &es, &ee);
|
||||
if (m < 0) {
|
||||
fprintf(stderr, "%s: internal error\n", PROJECT);
|
||||
return 1; /* internal error */
|
||||
}
|
||||
else if (m == 0) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "\033[00;33;01mline %2d: no side match\033[00m\n", (int) j);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "\033[00;33;01mline %2d: west: %d (\'%lc\') to %d (\'%lc\') [len %d]; "
|
||||
"east: %d (\'%lc\') to %d (\'%lc\') [len %d]\033[00m\n", (int) j,
|
||||
(int) (ws ? ws - input.lines[j].text->memory : 0), ws ? ws[0] : to_utf32('?'),
|
||||
(int) (we ? we - input.lines[j].text->memory - 1 : 0), we ? we[-1] : to_utf32('?'),
|
||||
(int) (ws && we ? (we - input.lines[j].text->memory - (ws - input.lines[j].text->memory)) : 0),
|
||||
(int) (es ? es - input.lines[j].text->memory : 0), es ? es[0] : to_utf32('?'),
|
||||
(int) (ee ? ee - input.lines[j].text->memory - 1 : 0), ee ? ee[-1] : to_utf32('?'),
|
||||
(int) (es && ee ? (ee - input.lines[j].text->memory - (es - input.lines[j].text->memory)) : 0));
|
||||
#endif
|
||||
if (ws && we) {
|
||||
did_something = 1;
|
||||
for (p = ws; p < we; ++p) {
|
||||
size_t idx = p - input.lines[j].text->memory; // TODO
|
||||
*p = char_space; // TODO should only blank visible characters, or length of string would change
|
||||
set_char_at(input.lines[j].mbtext, input.lines[j].posmap[idx], char_space);
|
||||
}
|
||||
}
|
||||
if (es && ee) {
|
||||
for (p = es; p < ee; ++p) {
|
||||
size_t idx = p - input.lines[j].text;
|
||||
*p = ' ';
|
||||
set_char_at(input.lines[j].mbtext, input.lines[j].posmap[idx], char_space);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove as many spaces from the left side of the line as the west side
|
||||
* of the box was wide. Don't do it if we never removed anything from the
|
||||
* west side. Don't harm the line's text if there aren't enough spaces.
|
||||
*/
|
||||
if (did_something) {
|
||||
for (j = textstart; j < textend; ++j) {
|
||||
size_t c;
|
||||
size_t widz = opt.design->shape[NW].width + opt.design->padding[BLEF];
|
||||
for (c = 0; c < widz; ++c) {
|
||||
if (input.lines[j].text[c] != ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if defined(DEBUG)
|
||||
fprintf(stderr, "memmove(\"%s\", \"%s\", %d);\n",
|
||||
input.lines[j].text, input.lines[j].text + c, (int) (input.lines[j].len - c + 1));
|
||||
#endif
|
||||
memmove(input.lines[j].text, input.lines[j].text + c,
|
||||
input.lines[j].len - c + 1); /* +1 for zero byte */
|
||||
input.lines[j].len -= c;
|
||||
|
||||
#if defined(DEBUG)
|
||||
fprintf(stderr, "u32_move(\"%s\", \"%s\", %d); // posmap[c]=%d\n",
|
||||
u32_strconv_to_output(input.lines[j].mbtext),
|
||||
u32_strconv_to_output(input.lines[j].mbtext + input.lines[j].posmap[c]),
|
||||
(int) (input.lines[j].num_chars - input.lines[j].posmap[c] + 1), (int) input.lines[j].posmap[c]);
|
||||
#endif
|
||||
u32_move(input.lines[j].mbtext, input.lines[j].mbtext + input.lines[j].posmap[c],
|
||||
input.lines[j].num_chars - input.lines[j].posmap[c] + 1); /* +1 for zero byte */
|
||||
input.lines[j].num_chars -= c;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if (!did_something) {
|
||||
fprintf(stderr, "There is nothing to remove (did_something == 0).\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Phase 4: Remove box top and body lines from input
|
||||
*/
|
||||
if (opt.killblank) {
|
||||
while (empty_line(input.lines + textstart) && textstart < textend) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Killing leading blank line in box body.\n");
|
||||
#endif
|
||||
++textstart;
|
||||
}
|
||||
while (empty_line(input.lines + textend - 1) && textend > textstart) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Killing trailing blank line in box body.\n");
|
||||
#endif
|
||||
--textend;
|
||||
}
|
||||
}
|
||||
|
||||
if (textstart > boxstart) {
|
||||
for (j = boxstart; j < textstart; ++j) BFREE (input.lines[j].text);
|
||||
memmove(input.lines + boxstart, input.lines + textstart,
|
||||
(input.num_lines - textstart) * sizeof(line_t));
|
||||
input.num_lines -= textstart - boxstart;
|
||||
textend -= textstart - boxstart;
|
||||
boxend -= textstart - boxstart;
|
||||
}
|
||||
if (boxend > textend) {
|
||||
for (j = textend; j < boxend; ++j) BFREE (input.lines[j].text);
|
||||
if (boxend < input.num_lines) {
|
||||
memmove(input.lines + textend, input.lines + boxend,
|
||||
(input.num_lines - boxend) * sizeof(line_t));
|
||||
}
|
||||
input.num_lines -= boxend - textend;
|
||||
}
|
||||
input.maxline = 0;
|
||||
for (j = 0; j < input.num_lines; ++j) {
|
||||
if (input.lines[j].len - input.lines[j].invis > input.maxline) {
|
||||
input.maxline = input.lines[j].len - input.lines[j].invis;
|
||||
}
|
||||
}
|
||||
memset(input.lines + input.num_lines, 0,
|
||||
(BMAX (textstart - boxstart, (size_t) 0) + BMAX (boxend - textend, (size_t) 0)) * sizeof(line_t));
|
||||
|
||||
#ifdef DEBUG
|
||||
print_input_lines(" (remove_box) after box removal");
|
||||
fprintf(stderr, "Number of lines shrunk by %d.\n",
|
||||
(int) (BMAX (textstart - boxstart, (size_t) 0) + BMAX (boxend - textend, (size_t) 0)));
|
||||
#endif
|
||||
|
||||
return 0; /* all clear */
|
||||
}
|
||||
|
||||
|
||||
|
||||
void output_input(const int trim_only)
|
||||
{
|
||||
size_t indent;
|
||||
int ntabs, nspcs;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "output_input() - enter (trim_only=%d)\n", trim_only);
|
||||
#endif
|
||||
for (size_t j = 0; j < input.num_lines; ++j) {
|
||||
if (input.lines[j].text == NULL) {
|
||||
continue;
|
||||
}
|
||||
bxstr_t *temp = bxs_rtrim(input.lines[j].text);
|
||||
bxs_free(input.lines[j].text);
|
||||
input.lines[j].text = temp;
|
||||
if (trim_only) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char *indentspc = NULL;
|
||||
if (opt.tabexp == 'u') {
|
||||
indent = input.lines[j].text->indent;
|
||||
ntabs = indent / opt.tabstop;
|
||||
nspcs = indent % opt.tabstop;
|
||||
indentspc = (char *) malloc(ntabs + nspcs + 1);
|
||||
if (indentspc == NULL) {
|
||||
perror(PROJECT);
|
||||
return;
|
||||
}
|
||||
memset(indentspc, (int) '\t', ntabs);
|
||||
memset(indentspc + ntabs, (int) ' ', nspcs);
|
||||
indentspc[ntabs + nspcs] = '\0';
|
||||
}
|
||||
else if (opt.tabexp == 'k') {
|
||||
uint32_t *indent32 = tabbify_indent(j, NULL, input.indent);
|
||||
indentspc = u32_strconv_to_output(indent32);
|
||||
BFREE(indent32);
|
||||
indent = input.indent;
|
||||
}
|
||||
else {
|
||||
indentspc = (char *) strdup("");
|
||||
indent = 0;
|
||||
}
|
||||
|
||||
char *outtext = u32_strconv_to_output(bxs_first_char_ptr(input.lines[j].text, indent));
|
||||
fprintf(opt.outfile, "%s%s%s", indentspc, outtext,
|
||||
(input.final_newline || j < input.num_lines - 1 ? opt.eol : ""));
|
||||
BFREE(outtext);
|
||||
BFREE(indentspc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* vim: set sw=4: */
|
@ -32,6 +32,7 @@ extern char *shape_name[];
|
||||
#define NUM_SHAPES 16
|
||||
|
||||
#define SHAPES_PER_SIDE 5
|
||||
#define CORNERS_PER_SIDE 2
|
||||
#define NUM_SIDES 4
|
||||
#define NUM_CORNERS 4
|
||||
|
||||
|
@ -350,7 +350,7 @@ uint32_t *u32_nspaces(const size_t n)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO It seems skip is always 0, can we remove that parameter?
|
||||
uint32_t *u32_strnrstr(const uint32_t *s1, const uint32_t *s2, const size_t s2_len, int skip)
|
||||
{
|
||||
if (is_empty(s2)) {
|
||||
|
@ -249,8 +249,8 @@ uint32_t *u32_strnrstr(const uint32_t *s1, const uint32_t *s2, const size_t s2_l
|
||||
|
||||
|
||||
/**
|
||||
* Insert `n` spaces at position `idx` into `s`. This modifies the given string.
|
||||
* @param s the string to modify
|
||||
* Insert `n` spaces at position `idx` into `*s`. This modifies the given string.
|
||||
* @param s **pointer to** the string to modify. `*s` will be `realloc()`ed if needed.
|
||||
* @param idx the position at which to insert. The character previously at this position will move to the right.
|
||||
* @param n the number of spaces to insert
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user