3b825735d8
Stateless and I stumbled upon this issue while discussing the semantics of read, accepting a size_t but only being able to return ssize_t, effectively lacking the ability to report successful reads > SSIZE_MAX. The discussion went along and we came to the topic of input-based memory allocations. Basically, it was possible for the argument to a memory-allocation-function to overflow, leading to a segfault later. The OpenBSD-guys came up with the ingenious reallocarray-function, and I implemented it as ereallocarray, which automatically returns on error. Read more about it here[0]. A simple testcase is this (courtesy to stateless): $ sbase-strings -n (2^(32|64) / 4) This will segfault before this patch and properly return an OOM- situation afterwards (thanks to the overflow-check in reallocarray). [0]: http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/calloc.3
228 lines
3.3 KiB
C
228 lines
3.3 KiB
C
/* See LICENSE file for copyright and license details. */
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "util.h"
|
|
|
|
#define NLINES 256
|
|
#define NCOLS 800
|
|
|
|
static char **buff;
|
|
|
|
static int obackspace, onotabs, ohalfline, oescape;
|
|
static unsigned nline, ncol, nchar, nspaces, maxline, bs;
|
|
static size_t pagsize = NLINES;
|
|
|
|
static void
|
|
flush(void)
|
|
{
|
|
int c;
|
|
unsigned i, j;
|
|
|
|
for (i = 0; i < maxline; ++i) {
|
|
for (j = 0; j < NCOLS && (c = buff[i][j]) != '\0'; ++j)
|
|
putchar(c);
|
|
putchar('\n');
|
|
}
|
|
bs = nchar = nline = ncol = 0;
|
|
}
|
|
|
|
static void
|
|
forward(unsigned n)
|
|
{
|
|
unsigned lim;
|
|
|
|
for (lim = ncol + n; ncol != lim && nchar < NCOLS-1; ++nchar) {
|
|
switch (buff[nline][nchar]) {
|
|
case '\b':
|
|
--ncol;
|
|
break;
|
|
case '\0':
|
|
buff[nline][nchar] = ' ';
|
|
/* FALLTHROUGH */
|
|
default:
|
|
++ncol;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
linefeed(int up, int rcarriage)
|
|
{
|
|
unsigned oncol = ncol;
|
|
|
|
nspaces = 0;
|
|
if (up > 0) {
|
|
if (nline == pagsize-1) {
|
|
flush();
|
|
} else {
|
|
if (++nline > maxline)
|
|
maxline = nline;
|
|
}
|
|
} else {
|
|
if (nline > 0)
|
|
--nline;
|
|
}
|
|
bs = 0;
|
|
if (rcarriage) {
|
|
forward(oncol);
|
|
nchar = ncol = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
newchar(int c)
|
|
{
|
|
char *cp;
|
|
|
|
forward(nspaces);
|
|
nspaces = 0;
|
|
|
|
switch (c) {
|
|
case ' ':
|
|
forward(1);
|
|
break;
|
|
case '\r':
|
|
nchar = ncol = 0;
|
|
break;
|
|
case '\t':
|
|
forward(8 - ncol%8);
|
|
break;
|
|
case '\b':
|
|
if (ncol > 0)
|
|
--ncol;
|
|
if (nchar > 0)
|
|
--nchar;
|
|
bs = 1;
|
|
break;
|
|
default:
|
|
cp = &buff[nline][nchar];
|
|
if (*cp != '\0' && *cp != ' ' && bs && !obackspace) {
|
|
if (nchar != NCOLS-3) {
|
|
memmove(cp + 3, cp + 1, NCOLS - nchar - 2);
|
|
cp[1] = '\b';
|
|
nchar += 2;
|
|
}
|
|
}
|
|
if (nchar != NCOLS-1) {
|
|
for (cp = buff[nline]; cp < &buff[nline][nchar]; ++cp) {
|
|
if (*cp == '\0')
|
|
*cp = ' ';
|
|
}
|
|
buff[nline][nchar++] = c;
|
|
++ncol;
|
|
}
|
|
bs = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
col(void)
|
|
{
|
|
int c;
|
|
|
|
while ((c = getchar()) != EOF) {
|
|
switch (c) {
|
|
case '\x1b':
|
|
switch (c = getchar()) {
|
|
case '8': /* reverse half-line-feed */
|
|
case '7': /* reverse line-feed */
|
|
linefeed(-1, 0);
|
|
continue;
|
|
case '9': /* forward half-line-feed */
|
|
if (ohalfline)
|
|
break;
|
|
linefeed(1, 0);
|
|
continue;
|
|
}
|
|
if (!oescape)
|
|
continue;
|
|
newchar('\x1b');
|
|
if (c != EOF)
|
|
newchar(c);
|
|
break;
|
|
case '\v':
|
|
linefeed(-1, 0);
|
|
break;
|
|
case ' ':
|
|
if (!onotabs) {
|
|
if (++nspaces != 8)
|
|
continue;
|
|
c = '\t';
|
|
nspaces = 0;
|
|
}
|
|
/* FALLTHROUGH */
|
|
case '\r':
|
|
case '\b':
|
|
case '\t':
|
|
newchar(c);
|
|
break;
|
|
case '\n':
|
|
linefeed(1, 1);
|
|
break;
|
|
default:
|
|
if (!iscntrl(c))
|
|
newchar(c);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
allocbuf(void)
|
|
{
|
|
char **bp;
|
|
|
|
buff = ereallocarray(buff, pagsize, sizeof(*buff));
|
|
for (bp = buff; bp < &buff[pagsize]; ++bp)
|
|
*bp = emalloc(NCOLS);
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
enprintf(2, "usage: %s [-p][-l num][-b][-f][-x]\n", argv0);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
ARGBEGIN {
|
|
case 'b':
|
|
obackspace = 1;
|
|
break;
|
|
case 'f':
|
|
ohalfline = 1;
|
|
break;
|
|
case 'l':
|
|
pagsize = estrtonum(EARGF(usage()), 0, SIZE_MAX);
|
|
break;
|
|
case 'p':
|
|
oescape = 1;
|
|
break;
|
|
case 'x':
|
|
onotabs = 1;
|
|
break;
|
|
default:
|
|
usage();
|
|
} ARGEND;
|
|
|
|
if (argc > 0)
|
|
usage();
|
|
|
|
allocbuf();
|
|
col();
|
|
flush();
|
|
|
|
if (ferror(stdin))
|
|
enprintf(1, "error reading input");
|
|
if (ferror(stdout))
|
|
enprintf(2, "error writing output");
|
|
|
|
return 0;
|
|
}
|