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