1 /*
2  *  R : A Computer Language for Statistical Data Analysis
3  *  Copyright (C) 1995, 1996, 1997,  Robert Gentleman and Ross Ihaka
4  *                2007-2020 The R Core Team
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, a copy is available at
18  *  https://www.R-project.org/Licenses/
19  *
20  *
21  *  I/O Support Code
22  *
23  *  This is a general IO support package to provide R with a uniform
24  *  interface to reading data from the console, files and internal
25  *  text strings.
26  *
27  *  This is probably overkill, but it works much better than the
28  *  previous anarchy.
29  */
30 
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34 
35 #include "IOStuff.h"
36 
37 
38 /* Move the iob->write_buf pointer to the next */
39 /* BufferListItem in the chain. If there no next */
40 /* buffer item, then one is added. */
41 
NextWriteBufferListItem(IoBuffer * iob)42 static int NextWriteBufferListItem(IoBuffer *iob)
43 {
44     if (iob->write_buf->next) {
45 	iob->write_buf = iob->write_buf->next;
46     }
47     else {
48 	BufferListItem *_new;
49 	if (!(_new = (BufferListItem*)malloc(sizeof(BufferListItem))))
50 	    return 0;
51 	_new->next = NULL;
52 	iob->write_buf->next = _new;
53 	iob->write_buf = iob->write_buf->next;
54     }
55     iob->write_ptr = iob->write_buf->buf;
56     iob->write_offset = 0;
57     return 1;
58 }
59 
60 /* Move the iob->read_buf pointer to the next */
61 /* BufferListItem in the chain. */
62 
NextReadBufferListItem(IoBuffer * iob)63 static int NextReadBufferListItem(IoBuffer *iob)
64 {
65     iob->read_buf = iob->read_buf->next;
66     iob->read_ptr = iob->read_buf->buf;
67     iob->read_offset = 0;
68     return 1;
69 }
70 
71 
72 /* Reset the read/write pointers of an IoBuffer */
73 
R_IoBufferWriteReset(IoBuffer * iob)74 int attribute_hidden R_IoBufferWriteReset(IoBuffer *iob)
75 {
76     if (iob == NULL || iob->start_buf == NULL)
77 	return 0;
78     iob->write_buf = iob->start_buf;
79     iob->write_ptr = iob->write_buf->buf;
80     iob->write_offset = 0;
81     iob->read_buf = iob->start_buf;
82     iob->read_ptr = iob->read_buf->buf;
83     iob->read_offset = 0;
84     return 1;
85 }
86 
87 /* Reset the read pointer of an IoBuffer */
88 
R_IoBufferReadReset(IoBuffer * iob)89 int attribute_hidden R_IoBufferReadReset(IoBuffer *iob)
90 {
91     if (iob == NULL || iob->start_buf == NULL)
92 	return 0;
93     iob->read_buf = iob->start_buf;
94     iob->read_ptr = iob->read_buf->buf;
95     iob->read_offset = 0;
96     return 1;
97 }
98 
99 /* Allocate an initial BufferListItem for IoBuffer */
100 /* Initialize the counts and pointers. */
101 
R_IoBufferInit(IoBuffer * iob)102 int attribute_hidden R_IoBufferInit(IoBuffer *iob)
103 {
104     if (iob == NULL) return 0;
105     iob->start_buf = (BufferListItem*)malloc(sizeof(BufferListItem));
106     if (iob->start_buf == NULL) return 0;
107     iob->start_buf->next = NULL;
108     return R_IoBufferWriteReset(iob);
109 }
110 
111 /* Free any BufferListItem's associated with an IoBuffer */
112 /* This resets pointers to NULL, which could be detected */
113 /* in other calls. */
114 
R_IoBufferFree(IoBuffer * iob)115 int attribute_hidden R_IoBufferFree(IoBuffer *iob)
116 {
117     BufferListItem *thisItem, *nextItem;
118     if (iob == NULL || iob->start_buf == NULL)
119 	return 0;
120     thisItem = iob->start_buf;
121     while (thisItem) {
122 	nextItem = thisItem->next;
123 	free(thisItem);
124 	thisItem = nextItem;
125     }
126     return 1;
127 }
128 
129 /* Add a character to an IoBuffer */
130 
R_IoBufferPutc(int c,IoBuffer * iob)131 int attribute_hidden R_IoBufferPutc(int c, IoBuffer *iob)
132 {
133     if (iob->write_offset == IOBSIZE)
134 	NextWriteBufferListItem(iob);
135     *(iob->write_ptr)++ = (char) c;
136     iob->write_offset++;
137     return 0;/*not used*/
138 }
139 
140 /* Add a (null terminated) string to an IoBuffer */
141 
R_IoBufferPuts(char * s,IoBuffer * iob)142 int attribute_hidden R_IoBufferPuts(char *s, IoBuffer *iob)
143 {
144     char *p;
145     int n = 0;
146     for (p = s; *p; p++) {
147 	R_IoBufferPutc(*p, iob);
148 	n++;
149     }
150     return n;
151 }
152 
153 /* Read a character from an IoBuffer */
154 
R_IoBufferGetc(IoBuffer * iob)155 int attribute_hidden R_IoBufferGetc(IoBuffer *iob)
156 {
157     if (iob->read_buf == iob->write_buf &&
158        iob->read_offset >= iob->write_offset)
159 	return EOF;
160     if (iob->read_offset == IOBSIZE) NextReadBufferListItem(iob);
161     iob->read_offset++;
162     return *(iob->read_ptr)++;
163 }
164 
165 /* What is our current offset, taking all blocks into account? */
166 
R_IoBufferReadOffset(IoBuffer * iob)167 int attribute_hidden R_IoBufferReadOffset(IoBuffer *iob)
168 {
169     int result = iob->read_offset;
170     BufferListItem* buf = iob->start_buf;
171     while(buf && buf != iob->read_buf) {
172     	result += IOBSIZE;
173     	buf = buf->next;
174     }
175     return result;
176 }
177 
178 /* Initialization code for text buffers */
179 
transferChars(unsigned char * p,const char * q)180 static void transferChars(unsigned char *p, const char *q)
181 {
182     while (*q) *p++ = *q++;
183     *p++ = '\n';
184     *p++ = '\0';
185 }
186 
187 /* respect encoding override from parser invocation - do_parse */
translateCharWithOverride(SEXP x)188 static const char *translateCharWithOverride(SEXP x)
189 {
190     if (!IS_LATIN1(x) && !mbcslocale && known_to_be_utf8)
191 	/* A hack to allow UTF-8 string literals, comments when
192 	   parsing on Windows. Note that the parser cannot handle
193 	   invalid characters when running in UTF-8 locale. */
194 	return CHAR(x);
195     else
196 	return translateChar(x);
197 }
198 
R_TextBufferInit(TextBuffer * txtb,SEXP text)199 int attribute_hidden R_TextBufferInit(TextBuffer *txtb, SEXP text)
200 {
201     int i, k, l, n;
202     if (isString(text)) {
203 	// translateChar might allocate
204 	void *vmax = vmaxget();
205 	n = length(text);
206 	l = 0;
207 	for (i = 0; i < n; i++) {
208 	    if (STRING_ELT(text, i) != R_NilValue) {
209 		k = (int) strlen(translateCharWithOverride(STRING_ELT(text, i)));
210 		if (k > l)
211 		    l = k;
212 	    }
213 	}
214 	vmaxset(vmax);
215 	txtb->vmax = vmax;
216 	txtb->buf = (unsigned char *)R_alloc(l+2, sizeof(char)); /* '\n' and '\0' */
217 	txtb->bufp = txtb->buf;
218 	txtb->text = text;
219 	txtb->ntext = n;
220 	txtb->offset = 0;
221 	transferChars(txtb->buf,
222 		      translateCharWithOverride(STRING_ELT(txtb->text, txtb->offset)));
223 	txtb->offset++;
224 	return 1;
225     }
226     else {
227 	txtb->vmax = vmaxget();
228 	txtb->buf = NULL;
229 	txtb->bufp = NULL;
230 	txtb->text = R_NilValue;
231 	txtb->ntext = 0;
232 	txtb->offset = 1;
233 	return 0;
234     }
235 }
236 
237 /* Finalization code for text buffers */
238 
R_TextBufferFree(TextBuffer * txtb)239 int attribute_hidden R_TextBufferFree(TextBuffer *txtb)
240 {
241     vmaxset(txtb->vmax);
242     return 0;/* not used */
243 }
244 
245 /* Getc for text buffers */
246 
R_TextBufferGetc(TextBuffer * txtb)247 int attribute_hidden R_TextBufferGetc(TextBuffer *txtb)
248 {
249     if (txtb->buf == NULL)
250 	return EOF;
251     if (*(txtb->bufp) == '\0') {
252 	if (txtb->offset == txtb->ntext) {
253 	    txtb->buf = NULL;
254 	    return EOF;
255 	} else {
256 	    const void *vmax = vmaxget();
257 	    transferChars(txtb->buf,
258 			  translateCharWithOverride(STRING_ELT(txtb->text,
259 			                                       txtb->offset)));
260 	    txtb->bufp = txtb->buf;
261 	    txtb->offset++;
262 	    vmaxset(vmax);
263 	}
264     }
265     return *txtb->bufp++;
266 }
267