#include "mtlib.h"
#include "file64.h"
#include <stdio.h>
#include "stdiom.h"
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/sysmacros.h>
#include <limits.h>
typedef struct memstream {
char *mstr_buf;
size_t mstr_alloc;
size_t mstr_pos;
size_t mstr_lsize;
char **mstr_ubufp;
size_t *mstr_usizep;
} memstream_t;
boolean_t
memstream_seek(size_t base, off_t off, size_t max, size_t *nposp)
{
size_t npos;
npos = base + (size_t)off;
if (off >= 0 && npos < base) {
return (B_FALSE);
}
if (off >= 0 && npos > LONG_MAX) {
return (B_FALSE);
}
if (off < 0 && npos >= base) {
return (B_FALSE);
}
if (npos > max) {
return (B_FALSE);
}
*nposp = npos;
return (B_TRUE);
}
int
memstream_newsize(size_t pos, size_t alloc, size_t nbytes, size_t *nallocp)
{
size_t npos = pos + nbytes + 1;
if (npos < pos) {
errno = EOVERFLOW;
return (-1);
}
if (npos > alloc) {
size_t newalloc = P2ROUNDUP(npos, BUFSIZ);
if (newalloc < npos) {
errno = EOVERFLOW;
return (-1);
}
*nallocp = newalloc;
return (1);
}
return (0);
}
static ssize_t
open_memstream_read(FILE *iop __unused, char *buf __unused,
size_t nbytes __unused)
{
errno = EBADF;
return (-1);
}
static ssize_t
open_memstream_write(FILE *iop, const char *buf, size_t nbytes)
{
memstream_t *memp = _xdata(iop);
size_t newsize;
int ret;
nbytes = MIN(nbytes, SSIZE_MAX);
ret = memstream_newsize(memp->mstr_pos, memp->mstr_alloc, nbytes,
&newsize);
if (ret < 0) {
return (-1);
} else if (ret > 0) {
void *temp;
temp = recallocarray(memp->mstr_buf, memp->mstr_alloc,
newsize, sizeof (char));
if (temp == NULL) {
return (-1);
}
memp->mstr_buf = temp;
memp->mstr_alloc = newsize;
*memp->mstr_ubufp = temp;
}
(void) memcpy(&memp->mstr_buf[memp->mstr_pos], buf, nbytes);
memp->mstr_pos += nbytes;
if (memp->mstr_pos > memp->mstr_lsize) {
memp->mstr_lsize = memp->mstr_pos;
memp->mstr_buf[memp->mstr_pos] = '\0';
}
*memp->mstr_usizep = MIN(memp->mstr_pos, memp->mstr_lsize);
return (nbytes);
}
static off_t
open_memstream_seek(FILE *iop, off_t off, int whence)
{
memstream_t *memp = _xdata(iop);
size_t base, npos;
switch (whence) {
case SEEK_SET:
base = 0;
break;
case SEEK_CUR:
base = memp->mstr_pos;
break;
case SEEK_END:
base = memp->mstr_lsize;
break;
default:
errno = EINVAL;
return (-1);
}
if (!memstream_seek(base, off, SSIZE_MAX, &npos)) {
errno = EINVAL;
return (-1);
}
memp->mstr_pos = npos;
*memp->mstr_usizep = MIN(memp->mstr_pos, memp->mstr_lsize);
return ((off_t)memp->mstr_pos);
}
static int
open_memstream_close(FILE *iop)
{
memstream_t *memp = _xdata(iop);
free(memp);
_xunassoc(iop);
return (0);
}
FILE *
open_memstream(char **bufp, size_t *sizep)
{
FILE *iop;
memstream_t *memp;
if (bufp == NULL || sizep == NULL) {
errno = EINVAL;
return (NULL);
}
memp = calloc(1, sizeof (memstream_t));
if (memp == NULL) {
return (NULL);
}
memp->mstr_alloc = BUFSIZ;
memp->mstr_buf = calloc(memp->mstr_alloc, sizeof (char));
if (memp->mstr_buf == NULL) {
goto cleanup;
}
memp->mstr_buf[0] = '\0';
memp->mstr_pos = 0;
memp->mstr_lsize = 0;
memp->mstr_ubufp = bufp;
memp->mstr_usizep = sizep;
iop = _findiop();
if (iop == NULL) {
goto cleanup;
}
#ifdef _LP64
iop->_flag = (iop->_flag & ~_DEF_FLAG_MASK) | _IOWRT;
#else
iop->_flag = _IOWRT;
#endif
if (_xassoc(iop, open_memstream_read, open_memstream_write,
open_memstream_seek, open_memstream_close, memp) != 0) {
goto cleanup;
}
_setorientation(iop, _BYTE_MODE);
SET_SEEKABLE(iop);
*memp->mstr_ubufp = memp->mstr_buf;
*memp->mstr_usizep = MIN(memp->mstr_pos, memp->mstr_lsize);
return (iop);
cleanup:
free(memp->mstr_buf);
free(memp);
return (NULL);
}