1
0
mirror of https://git.zap.org.au/git/trader.git synced 2024-07-21 16:14:14 -04:00

Add functions prepstr(), pr_left(), pr_center() and pr_right()

These functions allow multiple lines to be printed left-aligned, centered
or right-aligned, with automatic line-wrapping where needed.
This commit is contained in:
John Zaitseff 2011-08-12 14:28:15 +10:00
parent 3984468894
commit 2612eddf3d
2 changed files with 822 additions and 1 deletions

View File

@ -42,6 +42,29 @@ typedef struct txwin {
} txwin_t;
// Declarations for argument processing in prepstr()
#define MAXFMTARGS 8 // Maximum number of positional arguments
enum argument_type {
TYPE_NONE, // No type yet assigned
TYPE_INT, // int
TYPE_LONGINT, // long int
TYPE_DOUBLE, // double
TYPE_STRING // const char *
};
struct argument {
enum argument_type a_type;
union a {
int a_int;
long int a_longint;
double a_double;
const char *a_string;
} a;
};
/************************************************************************
* Global variable definitions *
************************************************************************/
@ -132,6 +155,40 @@ static void txresize (void);
#endif
/*
Function: prepstr_addch - Add a character to the prepstr buffer
Parameters: chbuf - Pointer to chtype pointer in which to store string
chbufsize - Pointer to number of chtype elements in chbuf
attr - Character rendition to use
maxlines - Maximum number of screen lines to use
maxwidth - Maximum width of each line, in chars
line - Pointer to current line number
width - Pointer to current line width
lastspc - Pointer to const char * pointer to last space
widthspc - Pointer to width just before last space
widthbuf - Pointer to buffer to store widths of each line
widthbufsize - Number of int elements in widthbuf
str - Pointer to const char * pointer to string
Returns: int - -1 on error (with errno set), 0 otherwise
This helper function adds the character **str to **chbuf, using attr as
the character rendition (attributes), incrementing both *str and *chbuf
and decrementing *chbufsize. If a string is too long for the current
line, a previous space in the current line is converted to a new line
(if possible), else a new line is inserted into the current location
(if not on the last line). *line, *width, *lastspc, *widthspc and
widthbuf[] are all updated appropriately.
*/
static int prepstr_addch (chtype *restrict *restrict chbuf,
int *restrict chbufsize, chtype attr,
int maxlines, int maxwidth, int *restrict line,
int *restrict width,
chtype *restrict *restrict lastspc,
int *restrict widthspc, int *restrict widthbuf,
int widthbufsize,
const char *restrict *restrict str);
/*
Function: txinput_fixup - Copy strings with fixup
Parameters: dest - Destination buffer of size BUFSIZE
@ -155,7 +212,8 @@ static void txinput_fixup (char *restrict dest, char *restrict src,
* Basic text input/output function definitions *
************************************************************************/
// These functions are documented in the file "intf.h"
/* These functions are documented either in the file "intf.h" or in the
comments above. */
/***********************************************************************/
@ -497,6 +555,643 @@ void txresize (void)
#endif // HANDLE_RESIZE_EVENTS
/***********************************************************************/
// prepstr_addch: Add a character to the prepstr buffer
int prepstr_addch (chtype *restrict *restrict chbuf, int *restrict chbufsize,
chtype attr, int maxlines, int maxwidth,
int *restrict line, int *restrict width,
chtype *restrict *restrict lastspc, int *restrict widthspc,
int *restrict widthbuf, int widthbufsize,
const char *restrict *restrict str)
{
if (*line < 0) {
// First character in buffer: start line 0
*line = 0;
}
if (**str == '\n') {
// Start a new line
if (*line < maxlines - 1) {
*(*chbuf)++ = '\n';
(*chbufsize)--;
}
widthbuf[*line] = *width;
*width = 0;
*lastspc = NULL;
*widthspc = 0;
(*line)++;
(*str)++;
} else if (*width == maxwidth) {
// Current line is now too long
if (! isspace(**str) && *lastspc != NULL && *line < maxlines - 1) {
// Break on the last space in this line
**lastspc = '\n';
widthbuf[*line] = *widthspc;
*width -= *widthspc + 1;
*lastspc = NULL;
*widthspc = 0;
(*line)++;
} else {
// Insert a new-line character (if not on last line)
if (*line < maxlines - 1) {
*(*chbuf)++ = '\n';
(*chbufsize)--;
}
widthbuf[*line] = *width;
*width = 0;
*lastspc = NULL;
*widthspc = 0;
(*line)++;
// Skip any following spaces
while (isspace(**str)) {
if (*(*str)++ == '\n') {
break;
}
}
}
} else {
// Insert an ordinary character into the output string
if (isspace(**str)) {
*lastspc = *chbuf;
*widthspc = *width;
}
*(*chbuf)++ = (unsigned char) **str | attr;
(*chbufsize)--;
(*width)++;
(*str)++;
}
return 0;
}
/***********************************************************************/
// prepstr: Prepare a string for printing to screen
int prepstr (chtype *restrict chbuf, int chbufsize, chtype attr_norm,
chtype attr_alt1, chtype attr_alt2, int maxlines, int maxwidth,
int *restrict widthbuf, int widthbufsize,
const char *restrict format, ...)
{
struct argument format_arg[MAXFMTARGS];
int num_format_args, arg_num;
const char *orig_format;
va_list args;
int line, width;
chtype *lastspc;
int widthspc;
chtype curattr;
int saved_errno;
assert(chbuf != NULL);
assert(chbufsize > 0);
assert(maxlines > 0);
assert(maxwidth > 0);
assert(widthbuf != NULL);
assert(widthbufsize >= maxlines);
assert(format != NULL);
/* Do a preliminary scan through the format parameter to determine
the types of each positional argument (conversion specifier). If
we did not support "%m$"-style specifiers, this would not be
necessary. */
orig_format = format;
memset(format_arg, 0, sizeof(format_arg));
num_format_args = 0;
arg_num = 0;
va_start(args, format);
while (*format != '\0') {
switch (*format++) {
case '^':
// Switch to a different character rendition
if (*format == '\0') {
goto error_inval;
} else {
format++;
}
break;
case '%':
// Process a conversion specifier
if (*format == '\0') {
goto error_inval;
} else if (*format == '%') {
format++;
} else {
enum argument_type arg_type = TYPE_NONE;
bool inspec = true;
bool flag_posn = false;
bool flag_long = false;
int count = 0;
while (inspec && *format != '\0') {
char c = *format++;
switch (c) {
case '0':
// Zero flag, or part of numeric count
if (count == 0)
goto error_inval;
count *= 10;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// Part of some numeric count
count = count * 10 + (c - '0');
break;
case '$':
// Fixed-position argument
if (flag_posn || count == 0)
goto error_inval;
if (count > MAXFMTARGS) {
errno = E2BIG;
goto error;
}
flag_posn = true;
arg_num = count - 1;
count = 0;
break;
case '\'':
// Use locale-specific thousands separator
break;
case 'l':
// Long length modifier
if (flag_long)
goto error_inval;
flag_long = true;
break;
case 'd':
// Insert an integer (int or long int)
arg_type = flag_long ? TYPE_LONGINT : TYPE_INT;
goto handlefmt;
case 'N':
// Insert a monetary amount (double)
if (flag_long)
goto error_inval;
arg_type = TYPE_DOUBLE;
goto handlefmt;
case 's':
// Insert a string (const char *)
if (flag_long)
goto error_inval;
arg_type = TYPE_STRING;
handlefmt:
if (arg_num >= MAXFMTARGS) {
errno = E2BIG;
goto error;
}
if (format_arg[arg_num].a_type == TYPE_NONE) {
format_arg[arg_num].a_type = arg_type;
} else if (format_arg[arg_num].a_type != arg_type) {
goto error_inval;
}
arg_num++;
num_format_args = MAX(num_format_args, arg_num);
inspec = false;
break;
default:
goto error_inval;
}
}
if (inspec)
goto error_inval;
}
break;
default:
// Process an ordinary character: do nothing for now
;
}
}
for (int i = 0; i < num_format_args; i++) {
switch (format_arg[i].a_type) {
case TYPE_INT:
format_arg[i].a.a_int = va_arg(args, int);
break;
case TYPE_LONGINT:
format_arg[i].a.a_longint = va_arg(args, long int);
break;
case TYPE_DOUBLE:
format_arg[i].a.a_double = va_arg(args, double);
break;
case TYPE_STRING:
format_arg[i].a.a_string = va_arg(args, const char *);
break;
default:
/* Cannot allow unused arguments, as we have no way of
knowing how much space they take (cf. int vs. long long
int). */
goto error_inval;
}
}
// Actually process the format parameter string
format = orig_format;
arg_num = 0;
curattr = attr_norm;
line = -1; // Current line number (0 = first)
width = 0; // Width of the current line
lastspc = NULL; // Pointer to last space in line
widthspc = 0; // Width of line before last space
while (*format != '\0' && chbufsize > 1 && line < maxlines) {
switch (*format) {
case '^':
// Switch to a different character rendition
if (*++format == '\0') {
goto error_inval;
} else {
switch (*format) {
case '^':
if (prepstr_addch(&chbuf, &chbufsize, curattr, maxlines,
maxwidth, &line, &width, &lastspc,
&widthspc, widthbuf, widthbufsize,
&format) < 0) {
goto error;
}
break;
case '{':
curattr = attr_alt1;
format++;
break;
case '[':
curattr = attr_alt2;
format++;
break;
case '}':
case ']':
curattr = attr_norm;
format++;
break;
default:
goto error_inval;
}
}
break;
case '%':
// Process a conversion specifier
if (*++format == '\0') {
goto error_inval;
} else if (*format == '%') {
if (prepstr_addch(&chbuf, &chbufsize, curattr, maxlines,
maxwidth, &line, &width, &lastspc, &widthspc,
widthbuf, widthbufsize, &format) < 0) {
goto error;
}
} else {
bool inspec = true;
bool flag_posn = false;
bool flag_long = false;
bool flag_thou = false;
int count = 0;
const char *str;
char *buf = malloc(BUFSIZE);
if (buf == NULL)
err_exit_nomem();
while (inspec && *format != '\0') {
char c = *format++;
switch (c) {
case '0':
// Zero flag, or part of numeric count
if (count == 0) {
// Zero flag is not supported
free(buf);
goto error_inval;
}
count *= 10;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// Part of some numeric count
count = count * 10 + (c - '0');
break;
case '$':
// Fixed-position argument
if (flag_posn || count == 0) {
free(buf);
goto error_inval;
}
if (count > MAXFMTARGS) {
free(buf);
errno = E2BIG;
goto error;
}
flag_posn = true;
arg_num = count - 1;
count = 0;
break;
case '\'':
// Use locale-specific thousands separator
if (flag_thou) {
free(buf);
goto error_inval;
}
flag_thou = true;
break;
case 'l':
// Long length modifier
if (flag_long) {
free(buf);
goto error_inval;
}
flag_long = true;
break;
case 'd':
// Insert an integer (int or long int) into the output
if (count != 0) {
free(buf);
goto error_inval;
}
if (arg_num >= MAXFMTARGS) {
free(buf);
errno = E2BIG;
goto error;
}
if (flag_long) {
if (snprintf(buf, BUFSIZE, flag_thou ? "%'ld" : "%ld",
format_arg[arg_num].a.a_longint) < 0) {
saved_errno = errno;
free(buf);
errno = saved_errno;
goto error;
}
} else {
if (snprintf(buf, BUFSIZE, flag_thou ? "%'d" : "%d",
format_arg[arg_num].a.a_int) < 0) {
saved_errno = errno;
free(buf);
errno = saved_errno;
goto error;
}
}
str = buf;
goto insertstr;
case 'N':
// Insert a monetary amount (double) into the output
if (count != 0 || flag_thou || flag_long) {
free(buf);
goto error_inval;
}
if (arg_num >= MAXFMTARGS) {
free(buf);
errno = E2BIG;
goto error;
}
if (l_strfmon(buf, BUFSIZE, "%n",
format_arg[arg_num].a.a_double) < 0) {
saved_errno = errno;
free(buf);
errno = saved_errno;
goto error;
}
str = buf;
goto insertstr;
case 's':
// Insert a string (const char *) into the output
if (count != 0 || flag_thou || flag_long) {
free(buf);
goto error_inval;
}
if (arg_num >= MAXFMTARGS) {
free(buf);
errno = E2BIG;
goto error;
}
str = format_arg[arg_num].a.a_string;
if (str == NULL) {
str = "(null)"; // As per GNU printf()
}
insertstr:
// Insert the string pointed to by str
while (*str != '\0' && chbufsize > 1 && line < maxlines) {
if (prepstr_addch(&chbuf, &chbufsize, curattr,
maxlines, maxwidth, &line, &width,
&lastspc, &widthspc, widthbuf,
widthbufsize, &str) < 0) {
saved_errno = errno;
free(buf);
errno = saved_errno;
goto error;
}
}
arg_num++;
inspec = false;
break;
default:
free(buf);
goto error_inval;
}
}
free(buf);
if (inspec)
goto error_inval;
}
break;
default:
// Process an ordinary character (including new-line)
if (prepstr_addch(&chbuf, &chbufsize, curattr, maxlines, maxwidth,
&line, &width, &lastspc, &widthspc, widthbuf,
widthbufsize, &format) < 0) {
goto error;
}
}
}
*chbuf = 0; // Terminating NUL byte
if (line >= 0 && line < maxlines) {
widthbuf[line] = width;
} else if (line >= maxlines) {
line = maxlines - 1;
}
va_end(args);
return line + 1;
error_inval:
errno = EINVAL;
error:
va_end(args);
return -1;
}
/***********************************************************************/
// pr_left: Print strings in chbuf left-aligned
int pr_left (WINDOW *win, int y, int x, const chtype *restrict chbuf,
int lines, const int *restrict widthbuf)
{
assert(win != NULL);
assert(chbuf != NULL);
assert(lines > 0);
assert(widthbuf != NULL);
wmove(win, y, x);
for ( ; *chbuf != '\0'; chbuf++) {
if (*chbuf == '\n') {
wmove(win, getcury(win) + 1, x);
} else {
waddch(win, *chbuf);
}
}
return OK;
}
/***********************************************************************/
// pr_center: Print strings in chbuf centered in window
int pr_center (WINDOW *win, int y, int offset, const chtype *restrict chbuf,
int lines, const int *restrict widthbuf)
{
int ln = 0;
assert(win != NULL);
assert(chbuf != NULL);
assert(lines > 0);
assert(widthbuf != NULL);
wmove(win, y, (getmaxx(win) - widthbuf[ln]) / 2 + offset);
for ( ; *chbuf != '\0'; chbuf++) {
if (*chbuf == '\n') {
if (ln++ >= lines) {
return ERR;
} else {
wmove(win, getcury(win) + 1,
(getmaxx(win) - widthbuf[ln]) / 2 + offset);
}
} else {
waddch(win, *chbuf);
}
}
return OK;
}
/***********************************************************************/
// pr_right: Print strings in chbuf right-aligned
int pr_right (WINDOW *win, int y, int x, const chtype *restrict chbuf,
int lines, const int *restrict widthbuf)
{
int ln = 0;
assert(win != NULL);
assert(chbuf != NULL);
assert(lines > 0);
assert(widthbuf != NULL);
wmove(win, y, x - widthbuf[ln]);
for ( ; *chbuf != '\0'; chbuf++) {
if (*chbuf == '\n') {
if (ln++ >= lines) {
return ERR;
} else {
wmove(win, getcury(win) + 1, x - widthbuf[ln]);
}
} else {
waddch(win, *chbuf);
}
}
return OK;
}
/***********************************************************************/
// attrpr: Print a string with a particular character rendition

View File

@ -254,6 +254,132 @@ extern int delalltxwin (void);
extern int txrefresh (void);
/*
Function: prepstr - Prepare a string for printing to screen
Parameters: chbuf - Pointer to chtype buffer in which to store string
chbufsize - Number of chtype elements in chbuf
attr_norm - Normal character rendition to use
attr_alt1 - First alternate character rendition to use
attr_alt2 - Second alternate character rendition to use
maxlines - Maximum number of screen lines to use
maxwidth - Maximum width of each line, in chars
widthbuf - Pointer to buffer to store widths of each line
widthbufsize - Number of int elements in widthbuf
format - Format string as described below
... - Arguments for the format string
Returns: int - Number of lines actually used, or -1 on error
This function converts the format string and following arguments into
chbuf, a chtype buffer that can be used for calls to pr_left(),
pr_center() and pr_right(). At most maxlines lines are used, each with
a maximum width of maxwidth. The actual widths of each resulting line
are stored in widthbuf (which must not be NULL). If maxlines is
greater than 1, lines are wrapped as needed.
The format string is similar to but more limited than printf(). In
particular, the following conversion specifiers are understood:
%% - Print the ASCII percent sign (ASCII code U+0025)
%s - Insert the next parameter as a string
%d - Insert the next parameter as an integer (type int)
%'d - Insert as an int, using the locale's thousands separator
%ld - Insert the next parameter as a long int
%'ld - Insert as a long int, using the locale's thousands separator
%N - Insert the next parameter as a double, using the locale's
national currency format (extension to printf())
Instead of using "%" to convert the next parameter, "%m$" can be used
to indicate fixed parameter m (where m is an integer from 1 to 8). For
example, "%4$s" inserts the fourth parameter after "format" as a string
into chbuf. As with printf(), using "%m$" together with ordinary "%"
forms is undefined. If "%m$" is used, no parameter m can be skipped.
Note that no other flag, field width, precision or length modifier
characters are recognised: if needed, these should be formatted FIRST
with snprintf(), then inserted using %s as appropriate.
In addition to the conversion specifiers, the following character
rendition flags are understood, where the "^" character is a literal
ASCII circumflex accent:
^^ - Print the circumflex accent (ASCII code U+005E)
^{ - Switch to using attr_alt1 character rendition (alternate mode 1)
^} - Switch to using attr_norm character rendition
^[ - Switch to using attr_alt2 character rendition (alternate mode 2)
^] - Switch to using attr_norm character rendition
Characters other than these are inserted as literals, except that '\n'
will force the start of a new line. By default, attr_norm is used as
the character rendition (attributes).
This function returns the actual number of lines used (from 0 to
maxlines), or -1 on error (with errno set to EINVAL for an invalid
format conversion specifier or argument).
*/
extern int prepstr (chtype *restrict chbuf, int chbufsize, chtype attr_norm,
chtype attr_alt1, chtype attr_alt2, int maxlines,
int maxwidth, int *restrict widthbuf, int widthbufsize,
const char *restrict format, ...);
/*
Function: pr_left - Print strings in chbuf left-aligned
Parameters: win - Window to use (should be curwin)
y - Line on which to print first string
x - Starting column number for each line
chbuf - chtype buffer as returned from prepstr()
lines - Number of lines in chbuf (as returned from prepstr())
widthbuf - Widths of each line (as returned from prepstr())
Returns: int - Error code OK
This function takes the strings in the chtype array chbuf and prints
them left-aligned in the window win. Note that wrefresh() is NOT
called.
*/
extern int pr_left (WINDOW *win, int y, int x, const chtype *restrict chbuf,
int lines, const int *restrict widthbuf);
/*
Function: pr_center - Print strings in chbuf centered in window
Parameters: win - Window to use (should be curwin)
y - Line on which to print first string
offset - Column offset to add to position for each line
chbuf - chtype buffer as returned from prepstr()
lines - Number of lines in chbuf (as returned from prepstr())
widthbuf - Widths of each line (as returned from prepstr())
Returns: int - ERR if more lines in chbuf[] than lines, else OK
This function takes the strings in the chtype array chbuf and prints
them centered in the window win, offset by the parameter offset. Note
that wrefresh() is NOT called. ERR is returned if there are more lines
in chbuf[] than are passed in the parameter lines.
*/
extern int pr_center (WINDOW *win, int y, int offset,
const chtype *restrict chbuf, int lines,
const int *restrict widthbuf);
/*
Function: pr_right - Print strings in chbuf right-aligned
Parameters: win - Window to use (should be curwin)
y - Line on which to print first string
x - Ending column number for each line
chbuf - chtype buffer as returned from prepstr()
lines - Number of lines in chbuf (as returned from prepstr())
widthbuf - Widths of each line (as returned from prepstr())
Returns: int - ERR if more lines in chbuf[] than lines, else OK
This function takes the strings in the chtype array chbuf and prints
them right-aligned in the window win, with each line ending at column
x. Note that wrefresh() is NOT called. ERR is returned if there are
more lines in chbuf[] than are passed in the parameter lines.
*/
extern int pr_right (WINDOW *win, int y, int x, const chtype *restrict chbuf,
int lines, const int *restrict widthbuf);
/*
Function: attrpr - Print a string with a particular character rendition
Parameters: win - Window to use (should be curwin)