sbase/col.c
FRIGN 3b825735d8 Implement reallocarray()
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
2015-03-10 21:23:36 +01:00

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;
}