The R Project SVN R

Rev

Rev 77730 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/*
 *  R : A Computer Language for Statistical Data Analysis
 *  Copyright (C) 1995, 1996, 1997,  Robert Gentleman and Ross Ihaka
 *                2007-2025 The R Core Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, a copy is available at
 *  https://www.R-project.org/Licenses/
 *
 *
 *  I/O Support Code
 *
 *  This is a general IO support package to provide R with a uniform
 *  interface to reading data from the console, files and internal
 *  text strings.
 *
 *  This is probably overkill, but it works much better than the
 *  previous anarchy.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "IOStuff.h"


/* Move the iob->write_buf pointer to the next */
/* BufferListItem in the chain. If there no next */
/* buffer item, then one is added. */

static int NextWriteBufferListItem(IoBuffer *iob)
{
    if (iob->write_buf->next) {
    iob->write_buf = iob->write_buf->next;
    }
    else {
    BufferListItem *_new;
    if (!(_new = (BufferListItem*)malloc(sizeof(BufferListItem))))
        return 0;
    _new->next = NULL;
    iob->write_buf->next = _new;
    iob->write_buf = iob->write_buf->next;
    }
    iob->write_ptr = iob->write_buf->buf;
    iob->write_offset = 0;
    return 1;
}

/* Move the iob->read_buf pointer to the next */
/* BufferListItem in the chain. */

static int NextReadBufferListItem(IoBuffer *iob)
{
    iob->read_buf = iob->read_buf->next;
    iob->read_ptr = iob->read_buf->buf;
    iob->read_offset = 0;
    return 1;
}


/* Reset the read/write pointers of an IoBuffer */

attribute_hidden int R_IoBufferWriteReset(IoBuffer *iob)
{
    if (iob == NULL || iob->start_buf == NULL)
    return 0;
    iob->write_buf = iob->start_buf;
    iob->write_ptr = iob->write_buf->buf;
    iob->write_offset = 0;
    iob->read_buf = iob->start_buf;
    iob->read_ptr = iob->read_buf->buf;
    iob->read_offset = 0;
    return 1;
}

/* Reset the read pointer of an IoBuffer */

attribute_hidden int R_IoBufferReadReset(IoBuffer *iob)
{
    if (iob == NULL || iob->start_buf == NULL)
    return 0;
    iob->read_buf = iob->start_buf;
    iob->read_ptr = iob->read_buf->buf;
    iob->read_offset = 0;
    return 1;
}

/* Allocate an initial BufferListItem for IoBuffer */
/* Initialize the counts and pointers. */

attribute_hidden int R_IoBufferInit(IoBuffer *iob)
{
    if (iob == NULL) return 0;
    iob->start_buf = (BufferListItem*)malloc(sizeof(BufferListItem));
    if (iob->start_buf == NULL) return 0;
    iob->start_buf->next = NULL;
    return R_IoBufferWriteReset(iob);
}

/* Free any BufferListItem's associated with an IoBuffer */
/* This resets pointers to NULL, which could be detected */
/* in other calls. */

attribute_hidden int R_IoBufferFree(IoBuffer *iob)
{
    BufferListItem *thisItem, *nextItem;
    if (iob == NULL || iob->start_buf == NULL)
    return 0;
    thisItem = iob->start_buf;
    while (thisItem) {
    nextItem = thisItem->next;
    free(thisItem);
    thisItem = nextItem;
    }
    return 1;
}

/* Add a character to an IoBuffer */

attribute_hidden int R_IoBufferPutc(int c, IoBuffer *iob)
{
    if (iob->write_offset == IOBSIZE)
    NextWriteBufferListItem(iob);
    *(iob->write_ptr)++ = (char) c;
    iob->write_offset++;
    return 0;/*not used*/
}

/* Add a (null terminated) string to an IoBuffer */

attribute_hidden int R_IoBufferPuts(char *s, IoBuffer *iob)
{
    char *p;
    int n = 0;
    for (p = s; *p; p++) {
    R_IoBufferPutc(*p, iob);
    n++;
    }
    return n;
}

/* Read a character from an IoBuffer */

attribute_hidden int R_IoBufferGetc(IoBuffer *iob)
{
    if (iob->read_buf == iob->write_buf &&
       iob->read_offset >= iob->write_offset)
    return EOF;
    if (iob->read_offset == IOBSIZE) NextReadBufferListItem(iob);
    iob->read_offset++;
    return *(iob->read_ptr)++;
}

/* What is our current offset, taking all blocks into account? */

attribute_hidden int R_IoBufferReadOffset(IoBuffer *iob)
{
    int result = iob->read_offset;
    BufferListItem* buf = iob->start_buf;
    while(buf && buf != iob->read_buf) {
        result += IOBSIZE;
        buf = buf->next;
    }
    return result;
}
    
/* Initialization code for text buffers */

static void transferChars(unsigned char *p, const char *q)
{
    while (*q) *p++ = *q++;
    *p++ = '\n';
    *p++ = '\0';
}

/* respect encoding override from parser invocation - do_parse */
static const char *translateCharWithOverride(SEXP x)
{
    if (!IS_LATIN1(x) && !mbcslocale && known_to_be_utf8)
    /* A hack to allow UTF-8 string literals, comments when
       parsing on Windows. Note that the parser cannot handle
       invalid characters when running in UTF-8 locale. */
    return CHAR(x);
    else
    return translateChar(x);
}

attribute_hidden int R_TextBufferInit(TextBuffer *txtb, SEXP text)
{
    int i, k, l, n;
    if (isString(text)) {
    // translateChar might allocate
    void *vmax = vmaxget();
    n = length(text);
    l = 0;
    for (i = 0; i < n; i++) {
        if (STRING_ELT(text, i) != R_NilValue) {
        k = (int) strlen(translateCharWithOverride(STRING_ELT(text, i)));
        if (k > l)
            l = k;
        }
    }
    vmaxset(vmax);
    txtb->vmax = vmax;
    txtb->buf = (unsigned char *)R_alloc(l+2, sizeof(char)); /* '\n' and '\0' */
    txtb->bufp = txtb->buf;
    txtb->text = text;
    txtb->ntext = n;
    txtb->offset = 0;
    transferChars(txtb->buf,
              translateCharWithOverride(STRING_ELT(txtb->text, txtb->offset)));
    txtb->offset++;
    return 1;
    }
    else {
    txtb->vmax = vmaxget();
    txtb->buf = NULL;
    txtb->bufp = NULL;
    txtb->text = R_NilValue;
    txtb->ntext = 0;
    txtb->offset = 1;
    return 0;
    }
}

/* Finalization code for text buffers */

attribute_hidden int R_TextBufferFree(TextBuffer *txtb)
{
    vmaxset(txtb->vmax);
    return 0;/* not used */
}

/* Getc for text buffers */

attribute_hidden int R_TextBufferGetc(TextBuffer *txtb)
{
    if (txtb->buf == NULL)
    return EOF;
    if (*(txtb->bufp) == '\0') {
    if (txtb->offset == txtb->ntext) {
        txtb->buf = NULL;
        return EOF;
    } else {
        const void *vmax = vmaxget();
        transferChars(txtb->buf,
              translateCharWithOverride(STRING_ELT(txtb->text,
                                                   txtb->offset)));
        txtb->bufp = txtb->buf;
        txtb->offset++;
        vmaxset(vmax);
    }
    }
    return *txtb->bufp++;
}