Add option to override line terminators #60

This commit is contained in:
Thomas Jensen 2021-04-10 15:32:52 +02:00
parent 8a6d0d3315
commit 139994f97f
No known key found for this signature in database
GPG Key ID: A4ACEE270D0FB7DB
10 changed files with 210 additions and 102 deletions

View File

@ -6,8 +6,8 @@
boxes \- text mode box and comment drawing filter boxes \- text mode box and comment drawing filter
.SH SYNOPSIS .SH SYNOPSIS
.B boxes .B boxes
[\-hlmrv] [\-a\ format] [\-d\ design] [\-f\ file] [\-i\ indent] [\-k\ bool] [\-hlmrv] [\-a\ format] [\-d\ design] [\-e\ eol] [\-f\ file] [\-i\ indent]
[\-n\ encoding] [\-p\ pad] [\-q query] [\-s\ size] [\-t\ tabopts] [\-k\ bool] [\-n\ encoding] [\-p\ pad] [\-q query] [\-s\ size] [\-t\ tabopts]
[infile [outfile]] [infile [outfile]]
.SH DESCRIPTION .SH DESCRIPTION
.I Boxes .I Boxes
@ -132,6 +132,25 @@ Design selection. The one argument of this option is the name of the design to
use. use.
.\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .\" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.TP 0.6i .TP 0.6i
.B \-e \fIeol\fP
Override line terminator.
.I eol
can be
.I CR\fP,
.I LF\fP, or
.I CRLF\fP.
The default is to use the system-specific line terminator, which means
.I CRLF
on Windows, and
.I LF
otherwise. This option should only be used in an emergency, because normally
the system-specific line terminator will be just fine. This option is
considered experimental, and may go away in a future version of
.I boxes\fP.
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 .B \-f \fIstring\fP
Use alternate config file. The one argument of this option is the name of a Use alternate config file. The one argument of this option is the name of a
valid valid

View File

@ -98,7 +98,7 @@ regulex.o: regulex.c regulex.h boxes.h tools.h unicode.h config.h | check_dir
getopt.o: misc/getopt.c misc/getopt.h | check_dir getopt.o: misc/getopt.c misc/getopt.h | check_dir
parser.o: parser.c boxes.h lex.yy.h parser.h parsing.h tools.h shape.h discovery.h config.h | check_dir parser.o: parser.c boxes.h lex.yy.h parser.h parsing.h tools.h shape.h discovery.h config.h | check_dir
lex.yy.o: lex.yy.c parser.h boxes.h parsing.h tools.h shape.h config.h | check_dir lex.yy.o: lex.yy.c parser.h boxes.h parsing.h tools.h shape.h config.h | check_dir
parsecode.o: parsecode.c parser.h boxes.h tools.h lex.yy.h config.h | check_dir parsecode.o: parsecode.c parser.h boxes.h tools.h lex.yy.h regulex.h unicode.h config.h | check_dir
package: $(BOXES_EXECUTABLE_NAME) package: $(BOXES_EXECUTABLE_NAME)
if [ -z "$(PKG_NAME)" ] ; then exit 1 ; fi if [ -z "$(PKG_NAME)" ] ; then exit 1 ; fi

View File

@ -34,6 +34,11 @@
#include <unitypes.h> #include <unitypes.h>
#include <uniwidth.h> #include <uniwidth.h>
#ifdef __MINGW32__
#include <fcntl.h> /* _O_BINARY */
#include <io.h> /* _setmode() */
#endif
#include "shape.h" #include "shape.h"
#include "boxes.h" #include "boxes.h"
#include "tools.h" #include "tools.h"
@ -69,19 +74,18 @@ opt_t opt; /* command line options */
input_t input = INPUT_INITIALIZER; /* input lines */ input_t input = INPUT_INITIALIZER; /* input lines */
/* _\|/_ /* _\|/_
(o o) (o o)
+----oOO-{_}-OOo------------------------------------------------------------+ +----oOO-{_}-OOo------------------------------------------------------------+
| F u n c t i o n s | | F u n c t i o n s |
+--------------------------------------------------------------------------*/ +--------------------------------------------------------------------------*/
static void usage(FILE *st)
/* /**
* Print usage information on stream st. * Print usage information on stream `st`.
* * @param st the stream to print to
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/ */
static void usage(FILE *st)
{ {
char *config_file = discover_config_file(0); char *config_file = discover_config_file(0);
@ -89,6 +93,8 @@ static void usage(FILE *st)
fprintf(st, " -a fmt alignment/positioning of text inside box [default: hlvt]\n"); 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, " -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, " -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 ? config_file : "none"); fprintf(st, " -f file configuration file [default: %s]\n", config_file != NULL ? config_file : "none");
fprintf(st, " -h print usage information\n"); fprintf(st, " -h print usage information\n");
fprintf(st, " -i mode indentation mode [default: box]\n"); fprintf(st, " -i mode indentation mode [default: box]\n");
@ -108,12 +114,11 @@ static void usage(FILE *st)
static void usage_short(FILE *st) /**
/* * Print abbreviated usage information on stream `st`.
* Print abbreviated usage information on stream st. * @param st the stream to print to
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/ */
static void usage_short(FILE *st)
{ {
fprintf(st, "Usage: %s [options] [infile [outfile]]\n", PROJECT); fprintf(st, "Usage: %s [options] [infile [outfile]]\n", PROJECT);
fprintf(st, "Try `%s -h' for more information.\n", PROJECT); fprintf(st, "Try `%s -h' for more information.\n", PROJECT);
@ -121,12 +126,11 @@ static void usage_short(FILE *st)
static void usage_long(FILE *st) /**
/* * Print usage information on stream `st`, including a header text.
* Print usage information on stream st, including a header text. * @param st the stream to print to
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/ */
static void usage_long(FILE *st)
{ {
fprintf(st, "%s - draws any kind of box around your text (or removes it)\n", PROJECT); 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, " Website: https://boxes.thomasjensen.com/\n");
@ -135,6 +139,26 @@ static void usage_long(FILE *st)
/**
* Set *stdout* to binary mode, so that we can control the line terminator. This function only ever does anything on
* Windows, because on Linux, we already do have control over line terminators.
* @return the *stdout* stream, reconfigured to binary if necessary
*/
static FILE *get_stdout_configured()
{
#ifdef __MINGW32__
if (opt.eol != NULL) {
int rc = _setmode(fileno(stdout), _O_BINARY);
if (rc == -1) {
perror(PROJECT);
}
}
#endif
return stdout;
}
static int validate_tag(char *tag) static int validate_tag(char *tag)
{ {
if (strcmp(tag, QUERY_ALL) == 0 || strcmp(tag, QUERY_UNDOC) == 0) { if (strcmp(tag, QUERY_ALL) == 0 || strcmp(tag, QUERY_UNDOC) == 0) {
@ -236,6 +260,7 @@ static int process_commandline(int argc, char *argv[])
*/ */
memset(&opt, 0, sizeof(opt_t)); memset(&opt, 0, sizeof(opt_t));
opt.tabstop = DEF_TABSTOP; opt.tabstop = DEF_TABSTOP;
opt.eol = EOL_DEFAULT;
opt.f = NULL; opt.f = NULL;
opt.tabexp = 'e'; opt.tabexp = 'e';
opt.killblank = -1; opt.killblank = -1;
@ -257,7 +282,7 @@ static int process_commandline(int argc, char *argv[])
* Parse Command Line * Parse Command Line
*/ */
do { do {
oc = getopt(argc, argv, "a:c:d:f:hi:k:lmn:p:q:rs:t:v"); oc = getopt(argc, argv, "a:c:d:e:f:hi:k:lmn:p:q:rs:t:v");
switch (oc) { switch (oc) {
@ -365,6 +390,22 @@ static int process_commandline(int argc, char *argv[])
opt.design_choice_by_user = 1; opt.design_choice_by_user = 1;
break; break;
case 'e':
/*
* EOL Override
*/
if (strcasecmp(optarg, "CRLF") == 0) {
opt.eol = "\r\n";
} else if (strcasecmp(optarg, "LF") == 0) {
opt.eol = "\n";
} else if (strcasecmp(optarg, "CR") == 0) {
opt.eol = "\r";
} else {
fprintf(stderr, "%s: invalid eol spec -- %s\n", PROJECT, optarg);
return 1;
}
break;
case 'f': case 'f':
/* /*
* Input File * Input File
@ -620,7 +661,7 @@ static int process_commandline(int argc, char *argv[])
*/ */
if (argv[optind] == NULL) { /* neither infile nor outfile given */ if (argv[optind] == NULL) { /* neither infile nor outfile given */
opt.infile = stdin; opt.infile = stdin;
opt.outfile = stdout; opt.outfile = get_stdout_configured();
} }
else if (argv[optind + 1] && argv[optind + 2]) { /* illegal third file */ else if (argv[optind + 1] && argv[optind + 2]) { /* illegal third file */
@ -642,13 +683,13 @@ static int process_commandline(int argc, char *argv[])
} }
if (argv[optind + 1] == NULL) { if (argv[optind + 1] == NULL) {
opt.outfile = stdout; /* no outfile given */ opt.outfile = get_stdout_configured(); /* no outfile given */
} }
else if (strcmp(argv[optind + 1], "-") == 0) { else if (strcmp(argv[optind + 1], "-") == 0) {
opt.outfile = stdout; /* use stdout for output */ opt.outfile = get_stdout_configured(); /* use stdout for output */
} }
else { else {
opt.outfile = fopen(argv[optind + 1], "w"); opt.outfile = fopen(argv[optind + 1], "wb");
if (opt.outfile == NULL) { if (opt.outfile == NULL) {
perror(PROJECT); perror(PROJECT);
if (opt.infile != stdin) { if (opt.infile != stdin) {
@ -683,6 +724,8 @@ static int process_commandline(int argc, char *argv[])
fprintf (stderr, "\n"); fprintf (stderr, "\n");
fprintf (stderr, "- Mend box: %d\n", opt.mend); fprintf (stderr, "- Mend box: %d\n", opt.mend);
fprintf (stderr, "- Design Definition W shape: %s\n", opt.cld? opt.cld: "n/a"); fprintf (stderr, "- Design Definition W shape: %s\n", opt.cld? opt.cld: "n/a");
fprintf (stderr, "- Line terminator used: %s\n",
strcmp(opt.eol, "\r\n") == 0 ? "CRLF" : (strcmp(opt.eol, "\r") == 0 ? "CR" : "LF"));
#endif #endif
return 0; return 0;
@ -942,7 +985,7 @@ static void print_tags(tagstats_t *tagstats, size_t num_tags)
} }
fprintf(opt.outfile, "%s (%d)", tagstats[tidx].tag, (int) tagstats[tidx].count); fprintf(opt.outfile, "%s (%d)", tagstats[tidx].tag, (int) tagstats[tidx].count);
} }
fprintf(opt.outfile, "\n"); fprintf(opt.outfile, "%s", opt.eol);
} }
} }
@ -990,13 +1033,12 @@ static int list_styles()
memset(&space, ' ', LINE_MAX_BYTES); memset(&space, ' ', LINE_MAX_BYTES);
space[LINE_MAX_BYTES] = '\0'; space[LINE_MAX_BYTES] = '\0';
fprintf(opt.outfile, "Complete Design Information for \"%s\":\n", fprintf(opt.outfile, "Complete Design Information for \"%s\":%s", d->name, opt.eol);
d->name);
fprintf(opt.outfile, "-----------------------------------"); fprintf(opt.outfile, "-----------------------------------");
for (i = strlen(d->name); i > 0; --i) { for (i = strlen(d->name); i > 0; --i) {
fprintf(opt.outfile, "-"); fprintf(opt.outfile, "-");
} }
fprintf(opt.outfile, "\n"); fprintf(opt.outfile, "%s", opt.eol);
fprintf(opt.outfile, "Alias Names: "); fprintf(opt.outfile, "Alias Names: ");
size_t aidx = 0; size_t aidx = 0;
@ -1007,66 +1049,66 @@ static int list_styles()
if (aidx == 0) { if (aidx == 0) {
fprintf(opt.outfile, "none"); fprintf(opt.outfile, "none");
} }
fprintf(opt.outfile, "\n"); fprintf(opt.outfile, "%s", opt.eol);
fprintf(opt.outfile, "Author: %s\n", fprintf(opt.outfile, "Author: %s%s",
d->author ? d->author : "(unknown author)"); d->author ? d->author : "(unknown author)", opt.eol);
fprintf(opt.outfile, "Original Designer: %s\n", fprintf(opt.outfile, "Original Designer: %s%s",
d->designer ? d->designer : "(unknown artist)"); d->designer ? d->designer : "(unknown artist)", opt.eol);
fprintf(opt.outfile, "Creation Date: %s\n", fprintf(opt.outfile, "Creation Date: %s%s",
d->created ? d->created : "(unknown)"); d->created ? d->created : "(unknown)", opt.eol);
fprintf(opt.outfile, "Current Revision: %s%s%s\n", fprintf(opt.outfile, "Current Revision: %s%s%s%s",
d->revision ? d->revision : "", d->revision ? d->revision : "",
d->revision && d->revdate ? " as of " : "", d->revision && d->revdate ? " as of " : "",
d->revdate ? d->revdate : (d->revision ? "" : "(unknown)")); d->revdate ? d->revdate : (d->revision ? "" : "(unknown)"), opt.eol);
fprintf(opt.outfile, "Configuration File: %s\n", d->defined_in); fprintf(opt.outfile, "Configuration File: %s%s", d->defined_in, opt.eol);
fprintf(opt.outfile, "Indentation Mode: "); fprintf(opt.outfile, "Indentation Mode: ");
switch (d->indentmode) { switch (d->indentmode) {
case 'b': case 'b':
fprintf(opt.outfile, "box (indent box)\n"); fprintf(opt.outfile, "box (indent box)%s", opt.eol);
break; break;
case 't': case 't':
fprintf(opt.outfile, "text (retain indentation inside of box)\n"); fprintf(opt.outfile, "text (retain indentation inside of box)%s", opt.eol);
break; break;
default: default:
fprintf(opt.outfile, "none (discard indentation)\n"); fprintf(opt.outfile, "none (discard indentation)%s", opt.eol);
break; break;
} }
fprintf(opt.outfile, "Replacement Rules: "); fprintf(opt.outfile, "Replacement Rules: ");
if (d->anz_reprules > 0) { if (d->anz_reprules > 0) {
for (i = 0; i < (int) d->anz_reprules; ++i) { for (i = 0; i < (int) d->anz_reprules; ++i) {
fprintf(opt.outfile, "%d. (%s) \"%s\" WITH \"%s\"\n", i + 1, fprintf(opt.outfile, "%d. (%s) \"%s\" WITH \"%s\"%s", i + 1,
d->reprules[i].mode == 'g' ? "glob" : "once", d->reprules[i].mode == 'g' ? "glob" : "once",
d->reprules[i].search, d->reprules[i].repstr); d->reprules[i].search, d->reprules[i].repstr, opt.eol);
if (i < (int) d->anz_reprules - 1) { if (i < (int) d->anz_reprules - 1) {
fprintf(opt.outfile, " "); fprintf(opt.outfile, " ");
} }
} }
} }
else { else {
fprintf(opt.outfile, "none\n"); fprintf(opt.outfile, "none%s", opt.eol);
} }
fprintf(opt.outfile, "Reversion Rules: "); fprintf(opt.outfile, "Reversion Rules: ");
if (d->anz_revrules > 0) { if (d->anz_revrules > 0) {
for (i = 0; i < (int) d->anz_revrules; ++i) { for (i = 0; i < (int) d->anz_revrules; ++i) {
fprintf(opt.outfile, "%d. (%s) \"%s\" TO \"%s\"\n", i + 1, fprintf(opt.outfile, "%d. (%s) \"%s\" TO \"%s\"%s", i + 1,
d->revrules[i].mode == 'g' ? "glob" : "once", d->revrules[i].mode == 'g' ? "glob" : "once",
d->revrules[i].search, d->revrules[i].repstr); d->revrules[i].search, d->revrules[i].repstr, opt.eol);
if (i < (int) d->anz_revrules - 1) { if (i < (int) d->anz_revrules - 1) {
fprintf(opt.outfile, " "); fprintf(opt.outfile, " ");
} }
} }
} }
else { else {
fprintf(opt.outfile, "none\n"); fprintf(opt.outfile, "none%s", opt.eol);
} }
fprintf(opt.outfile, "Minimum Box Dimensions: %d x %d (width x height)\n", fprintf(opt.outfile, "Minimum Box Dimensions: %d x %d (width x height)%s",
(int) d->minwidth, (int) d->minheight); (int) d->minwidth, (int) d->minheight, opt.eol);
fprintf(opt.outfile, "Default Padding: "); fprintf(opt.outfile, "Default Padding: ");
if (d->padding[BTOP] || d->padding[BRIG] if (d->padding[BTOP] || d->padding[BRIG]
@ -1092,15 +1134,14 @@ static int list_styles()
if (d->padding[BBOT]) { if (d->padding[BBOT]) {
fprintf(opt.outfile, "bottom %d", d->padding[BBOT]); fprintf(opt.outfile, "bottom %d", d->padding[BBOT]);
} }
fprintf(opt.outfile, "\n"); fprintf(opt.outfile, "%s", opt.eol);
} }
else { else {
fprintf(opt.outfile, "none\n"); fprintf(opt.outfile, "none%s", opt.eol);
} }
fprintf(opt.outfile, "Default Killblank: %s\n", fprintf(opt.outfile, "Default Killblank: %s%s",
empty_side(opt.design->shape, BTOP) && empty_side(opt.design->shape, BTOP) && empty_side(opt.design->shape, BBOT) ? "no" : "yes", opt.eol);
empty_side(opt.design->shape, BBOT) ? "no" : "yes");
fprintf(opt.outfile, "Tags: "); fprintf(opt.outfile, "Tags: ");
size_t tidx = 0; size_t tidx = 0;
@ -1111,7 +1152,7 @@ static int list_styles()
if (tidx == 0) { if (tidx == 0) {
fprintf(opt.outfile, "none"); fprintf(opt.outfile, "none");
} }
fprintf(opt.outfile, "\n"); fprintf(opt.outfile, "%s", opt.eol);
fprintf(opt.outfile, "Elastic Shapes: "); fprintf(opt.outfile, "Elastic Shapes: ");
sstart = 0; sstart = 0;
@ -1124,13 +1165,13 @@ static int list_styles()
sstart = 1; sstart = 1;
} }
} }
fprintf(opt.outfile, "\n"); fprintf(opt.outfile, "%s", opt.eol);
/* /*
* Display all shapes * Display all shapes
*/ */
if (query_is_undoc()) { if (query_is_undoc()) {
fprintf(opt.outfile, "Sample:\n%s\n", d->sample); fprintf(opt.outfile, "Sample:%s%s%s", opt.eol, d->sample, opt.eol);
} }
else { else {
int first_shape = 1; int first_shape = 1;
@ -1140,11 +1181,12 @@ static int list_styles()
} }
for (w = 0; w < d->shape[i].height; ++w) { for (w = 0; w < d->shape[i].height; ++w) {
char *escaped_line = escape(d->shape[i].chars[w], d->shape[i].width); char *escaped_line = escape(d->shape[i].chars[w], d->shape[i].width);
fprintf(opt.outfile, "%-24s%3s%c \"%s\"%c\n", fprintf(opt.outfile, "%-24s%3s%c \"%s\"%c%s",
(first_shape == 1 && w == 0 ? "Defined Shapes:" : ""), (first_shape == 1 && w == 0 ? "Defined Shapes:" : ""),
(w == 0 ? shape_name[i] : ""), (w == 0 ? ':' : ' '), (w == 0 ? shape_name[i] : ""), (w == 0 ? ':' : ' '),
escaped_line, escaped_line,
(w < d->shape[i].height - 1 ? ',' : ' ') (w < d->shape[i].height - 1 ? ',' : ' '),
opt.eol
); );
BFREE (escaped_line); BFREE (escaped_line);
} }
@ -1166,18 +1208,23 @@ static int list_styles()
for (i = 0; i < anz_designs; ++i) { for (i = 0; i < anz_designs; ++i) {
char *all_names = names(list[i]); char *all_names = names(list[i]);
if (list[i]->author && list[i]->designer && strcmp(list[i]->author, list[i]->designer) != 0) { if (list[i]->author && list[i]->designer && strcmp(list[i]->author, list[i]->designer) != 0) {
fprintf(opt.outfile, "%s\n%s, coded by %s:\n\n%s\n\n", all_names, fprintf(opt.outfile, "%s%s%s, coded by %s:%s%s%s%s%s", all_names, opt.eol,
list[i]->designer, list[i]->author, list[i]->sample); list[i]->designer, list[i]->author, opt.eol, opt.eol,
list[i]->sample, opt.eol, opt.eol);
} }
else if (list[i]->designer) { else if (list[i]->designer) {
fprintf(opt.outfile, "%s\n%s:\n\n%s\n\n", all_names, list[i]->designer, list[i]->sample); fprintf(opt.outfile, "%s%s%s:%s%s%s%s%s", all_names, opt.eol,
list[i]->designer, opt.eol, opt.eol,
list[i]->sample, opt.eol, opt.eol);
} }
else if (list[i]->author) { else if (list[i]->author) {
fprintf(opt.outfile, "%s\nunknown artist, coded by %s:\n\n%s\n\n", all_names, fprintf(opt.outfile, "%s%sunknown artist, coded by %s:%s%s%s%s%s", all_names, opt.eol,
list[i]->author, list[i]->sample); list[i]->author, opt.eol, opt.eol,
list[i]->sample, opt.eol, opt.eol);
} }
else { else {
fprintf(opt.outfile, "%s:\n\n%s\n\n", all_names, list[i]->sample); fprintf(opt.outfile, "%s:%s%s%s%s%s", all_names, opt.eol, opt.eol,
list[i]->sample, opt.eol, opt.eol);
} }
BFREE(all_names); BFREE(all_names);
@ -1204,9 +1251,9 @@ static int query_by_tag()
} }
for (int i = 0; i < anz_designs; ++i) { for (int i = 0; i < anz_designs; ++i) {
if (filter_by_tag(list[i]->tags)) { if (filter_by_tag(list[i]->tags)) {
fprintf(opt.outfile, "%s\n", list[i]->name); fprintf(opt.outfile, "%s%s", list[i]->name, opt.eol);
for (size_t aidx = 0; list[i]->aliases[aidx] != NULL; ++aidx) { for (size_t aidx = 0; list[i]->aliases[aidx] != NULL; ++aidx) {
fprintf(opt.outfile, "%s (alias)\n", list[i]->aliases[aidx]); fprintf(opt.outfile, "%s (alias)%s", list[i]->aliases[aidx], opt.eol);
} }
} }
} }

View File

@ -119,11 +119,20 @@ extern design_t *designs;
extern int anz_designs; extern int anz_designs;
/* system default line terminator */
#ifdef __MINGW32__
#define EOL_DEFAULT "\r\n"
#else
#define EOL_DEFAULT "\n"
#endif
typedef struct { /* Command line options: */ typedef struct { /* Command line options: */
int l; /** list available designs */ int l; /** list available designs */
char *f; /** the string specified as argument to -f ; config file path */ char *f; /** the string specified as argument to -f ; config file path */
int mend; /** 1 if -m is given, 2 in 2nd loop */ int mend; /** 1 if -m is given, 2 in 2nd loop */
char **query; /** parsed tag query expression passed in via -q; also, special handling of web UI needs */ char **query; /** parsed tag query expression passed in via -q; also, special handling of web UI needs */
char *eol; /** line break to use. Never NULL, default to EOL_DEFAULT. */
int r; /** remove box from input */ int r; /** remove box from input */
int tabstop; /** tab stop distance */ int tabstop; /** tab stop distance */
char tabexp; /** tab expansion mode (for leading tabs) */ char tabexp; /** tab expansion mode (for leading tabs) */

View File

@ -1012,7 +1012,7 @@ int output_box(const sentry_t *thebox)
BFREE (restored_indent); BFREE (restored_indent);
} }
fprintf(opt.outfile, "%s%s", obuf, (input.final_newline || j < nol - skip_end - 1 ? "\n" : "")); fprintf(opt.outfile, "%s%s", obuf, (input.final_newline || j < nol - skip_end - 1 ? opt.eol : ""));
} }
BFREE (indentspc); BFREE (indentspc);

View File

@ -30,6 +30,8 @@
#include "discovery.h" #include "discovery.h"
#include "tools.h" #include "tools.h"
#include "regulex.h"
#include "unicode.h"
#include "parsecode.h" #include "parsecode.h"
#include "parsing.h" #include "parsing.h"
#include "parser.h" #include "parser.h"
@ -42,6 +44,8 @@
*/ */
#define LEX_SDELIM "\"~'`!@%&*=:;<>?/|.\\" #define LEX_SDELIM "\"~'`!@%&*=:;<>?/|.\\"
static pcre2_code *eol_pattern = NULL;
static void chg_strdelims (pass_to_bison *bison_args, const char asesc, const char asdel) static void chg_strdelims (pass_to_bison *bison_args, const char asesc, const char asdel)
@ -924,4 +928,47 @@ int action_add_alias(pass_to_bison *bison_args, char *alias_name)
} }
static char *adjust_eols(char *sample)
{
if (eol_pattern == NULL) {
eol_pattern = compile_pattern("(?(?=\r)(\r\n?)|(\n))");
}
uint32_t *u32_sample = u32_strconv_from_input(sample);
uint32_t *replaced = regex_replace(eol_pattern, opt.eol, u32_sample, strlen(sample), 1);
char *result = u32_strconv_to_output(replaced);
BFREE(replaced);
BFREE(u32_sample);
return result;
}
int action_sample_block(pass_to_bison *bison_args, char *sample)
{
#ifdef PARSER_DEBUG
fprintf(stderr, "SAMPLE block rule satisfied\n");
#endif
if (curdes.sample) {
yyerror(bison_args, "duplicate SAMPLE block");
return RC_ERROR;
}
char *p = sample;
while ((*p == '\r' || *p == '\n') && *p != '\0') {
p++;
}
char *line = adjust_eols(p);
if (line == NULL) {
perror(PROJECT);
return RC_ABORT;
}
curdes.sample = line;
++(bison_args->num_mandatory);
return RC_SUCCESS;
}
/*EOF*/ /* vim: set cindent sw=4: */ /*EOF*/ /* vim: set cindent sw=4: */

View File

@ -148,6 +148,17 @@ void recover(pass_to_bison *bison_args);
int tag_record(pass_to_bison *bison_args, char *tag); int tag_record(pass_to_bison *bison_args, char *tag);
/**
* Add the sample block to the current design.
* @param bison_args the parser state
* @param sample the sample block content (non-empty when this is invoked)
* @return 0: success;
* 1: YYERROR must be invoked
* 2: YYABORT must be invoked
*/
int action_sample_block(pass_to_bison *bison_args, char *sample);
#endif #endif
/*EOF*/ /* vim: set cindent sw=4: */ /*EOF*/ /* vim: set cindent sw=4: */

View File

@ -277,32 +277,7 @@ entry: KEYWORD STRING
block: YSAMPLE STRING YENDSAMPLE block: YSAMPLE STRING YENDSAMPLE
{ {
/* invoke_action(action_sample_block(bison_args, $2));
* SAMPLE block (STRING is non-empty if we get here)
*/
char *line;
#ifdef PARSER_DEBUG
fprintf (stderr, "SAMPLE block rule satisfied\n");
#endif
if (curdes.sample) {
yyerror(bison_args, "duplicate SAMPLE block");
YYERROR;
}
char *p = $2;
while ((*p == '\r' || *p == '\n') && *p != '\0') {
p++;
}
line = (char *) strdup (p);
if (line == NULL) {
perror (PROJECT);
YYABORT;
}
curdes.sample = line;
++(bison_args->num_mandatory);
} }
| YSHAPES '{' slist '}' | YSHAPES '{' slist '}'

View File

@ -80,26 +80,26 @@ void print_design_list_header()
fprintf(opt.outfile, "%d Available Style%s", anz_designs, anz_designs == 1 ? "" : "s"); fprintf(opt.outfile, "%d Available Style%s", anz_designs, anz_designs == 1 ? "" : "s");
if (num_parent_configs > 0) { if (num_parent_configs > 0) {
fprintf(opt.outfile, ":\n"); fprintf(opt.outfile, ":%s", opt.eol);
fprintf(opt.outfile, "-----------------%s", anz_designs == 1 ? "" : "-"); fprintf(opt.outfile, "-----------------%s", anz_designs == 1 ? "" : "-");
for (int i = strlen(buf); i > 0; --i) { for (int i = strlen(buf); i > 0; --i) {
fprintf(opt.outfile, "-"); fprintf(opt.outfile, "-");
} }
fprintf(opt.outfile, "\n\n"); fprintf(opt.outfile, "%s%s", opt.eol, opt.eol);
fprintf(opt.outfile, "Configuration Files:\n"); fprintf(opt.outfile, "Configuration Files:%s", opt.eol);
fprintf(opt.outfile, " - %s\n", first_config_file); fprintf(opt.outfile, " - %s%s", first_config_file, opt.eol);
for (size_t i = 0; i < num_parent_configs; i++) { for (size_t i = 0; i < num_parent_configs; i++) {
fprintf(opt.outfile, " - %s (parent)\n", parent_configs[i]); fprintf(opt.outfile, " - %s (parent)%s", parent_configs[i], opt.eol);
} }
} }
else { else {
fprintf(opt.outfile, " in \"%s\":\n", first_config_file); fprintf(opt.outfile, " in \"%s\":%s", first_config_file, opt.eol);
fprintf(opt.outfile, "-----------------------%s", anz_designs == 1 ? "" : "-"); fprintf(opt.outfile, "-----------------------%s", anz_designs == 1 ? "" : "-");
for (int i = strlen(first_config_file) + strlen(buf); i > 0; --i) { for (int i = strlen(first_config_file) + strlen(buf); i > 0; --i) {
fprintf(opt.outfile, "-"); fprintf(opt.outfile, "-");
} }
} }
fprintf(opt.outfile, "\n\n"); fprintf(opt.outfile, "%s%s", opt.eol, opt.eol);
} }

View File

@ -1103,7 +1103,7 @@ void output_input(const int trim_only)
} }
fprintf(opt.outfile, "%s%s%s", indentspc, u32_strconv_to_output(advance32(input.lines[j].mbtext, indent)), fprintf(opt.outfile, "%s%s%s", indentspc, u32_strconv_to_output(advance32(input.lines[j].mbtext, indent)),
(input.final_newline || j < input.anz_lines - 1 ? "\n" : "")); (input.final_newline || j < input.anz_lines - 1 ? opt.eol : ""));
BFREE (indentspc); BFREE (indentspc);
} }
} }