-
Notifications
You must be signed in to change notification settings - Fork 392
/
omrfilestreamtext.c
347 lines (303 loc) · 11.8 KB
/
omrfilestreamtext.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/*******************************************************************************
* Copyright (c) 2015, 2021 IBM Corp. and others
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which accompanies this
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
* or the Apache License, Version 2.0 which accompanies this distribution and
* is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* This Source Code may also be made available under the following
* Secondary Licenses when the conditions for such availability set
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
* General Public License, version 2 with the GNU Classpath
* Exception [1] and GNU General Public License, version 2 with the
* OpenJDK Assembly Exception [2].
*
* [1] https://www.gnu.org/software/classpath/license.html
* [2] http://openjdk.java.net/legal/assembly-exception.html
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
*******************************************************************************/
/**
* @file omrfilestreamtext.c
* @ingroup Port
* @brief Buffered file text input and output through streams.
*
* This file implements stream based file IO with the ability to convert to different file
* encodings.
*/
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include "omriconvhelpers.h"
#include "omrport.h"
#include "omrstdarg.h"
#include "portnls.h"
#include "ut_omrport.h"
#if defined(J9ZOS390)
/* Needed for a translation table from ascii -> ebcdic */
#if !defined(OMR_EBCDIC)
#include "atoe.h"
#endif
/* a2e overrides many functions to use ASCII strings.
* We need the native EBCDIC string
*/
#if defined (nl_langinfo)
#undef nl_langinfo
#endif
#endif /* defined(J9ZOS390) */
/**
* CRLFNEWLINES will be defined when we require changing newlines from '\n' to '\r\n'
*/
#undef CRLFNEWLINES
#if defined(OMR_OS_WINDOWS)
#define CRLFNEWLINES
#endif /* defined(OMR_OS_WINDOWS) */
static intptr_t
write_all(struct OMRPortLibrary *portLibrary, OMRFileStream *fileStream, const char *buf, intptr_t nbytes);
static intptr_t
transliterate_write_text(struct OMRPortLibrary *portLibrary, OMRFileStream *fileStream, const char *buf, intptr_t nbytes, int32_t toEncoding);
/**
* Write formatted text to a file.
*
* Writes formatted output to the filestream. All text will be transliterated from modifed UTF-8
* to the platform encoding.
*
* @param[in] portLibrary The port library
* @param[in] fileStream Filestream to write to
* @param[in] format The format string.
* @param[in] args Variable argument list.
*
* @internal @note Supported, portable format specifiers are described in the document entitled
* "PortLibrary printf" in the "Inside J9" Lotus Notes database.
*/
void
omrfilestream_vprintf(struct OMRPortLibrary *portLibrary, OMRFileStream *fileStream, const char *format, va_list args)
{
char localBuffer[512];
char *writeBuffer = NULL;
uintptr_t bufferSize = 0;
uintptr_t stringSize = 0;
Trc_PRT_filestream_vprintf_Entry(fileStream, format);
if ((NULL == fileStream) || (NULL == format)) {
Trc_PRT_filestream_vprintf_invalidArgs(fileStream, format);
Trc_PRT_filestream_vprintf_Exit();
return;
}
/* str_vprintf(..,NULL,..) result is size of buffer required including the null terminator */
bufferSize = portLibrary->str_vprintf(portLibrary, NULL, 0, format, args);
/* use local buffer if possible, allocate a buffer from system memory if local buffer not large enough */
if (sizeof(localBuffer) >= bufferSize) {
writeBuffer = localBuffer;
} else {
Trc_PRT_filestream_vprintf_stackBufferNotBigEnough(fileStream, format, bufferSize);
writeBuffer = portLibrary->mem_allocate_memory(portLibrary, bufferSize, OMR_GET_CALLSITE(), OMRMEM_CATEGORY_PORT_LIBRARY);
}
/* format and write out the buffer (truncate into local buffer as last resort) */
if (NULL != writeBuffer) {
stringSize = portLibrary->str_vprintf(portLibrary, writeBuffer, bufferSize, format, args);
portLibrary->filestream_write_text(portLibrary, fileStream, writeBuffer, stringSize, J9STR_CODE_PLATFORM_RAW);
/* dispose of buffer if not on local */
if (writeBuffer != localBuffer) {
portLibrary->mem_free_memory(portLibrary, writeBuffer);
}
} else {
Trc_PRT_filestream_vprintf_failedMalloc(fileStream, format, bufferSize);
portLibrary->nls_printf(portLibrary, J9NLS_ERROR, J9NLS_PORT_FILE_MEMORY_ALLOCATE_FAILURE);
stringSize = portLibrary->str_vprintf(portLibrary, localBuffer, sizeof(localBuffer), format, args);
portLibrary->filestream_write_text(portLibrary, fileStream, localBuffer, stringSize, J9STR_CODE_PLATFORM_RAW);
}
Trc_PRT_filestream_vprintf_Exit();
}
/**
* Write formatted text to a filestream.
*
* Writes formatted output to the filestream. All text will be transliterated from modifed UTF-8
* to the platform encoding.
*
* @param[in] portLibrary The port library
* @param[in] fileStream Filestream to write to
* @param[in] format The format string to be output.
* @param[in] ... arguments for format.
*
* @internal @note Supported, portable format specifiers are described in the document entitled
* "PortLibrary printf" in the "Inside J9" Lotus Notes database.
*/
void
omrfilestream_printf(struct OMRPortLibrary *portLibrary, OMRFileStream *fileStream, const char *format, ...)
{
va_list args;
Trc_PRT_filestream_printf_Entry(fileStream, format);
va_start(args, format);
portLibrary->filestream_vprintf(portLibrary, fileStream, format, args);
va_end(args);
Trc_PRT_filestream_printf_Exit();
}
/**
* Write text to a filestream. This function will take a MUTF-8 encoded string, and convert it into
* the specified encoding. This makes it suitable for printing human readable text to the console
* or a file.
*
* Writes up to nbytes from the provided buffer to the file referenced by the filestream.
*
* @param[in] portLibrary The port library
* @param[in] fileStream Filestream to write.
* @param[in] buf Buffer to be written.
* @param[in] nbytes Size of buffer. Must fit in a size_t. Must be positive.
* @param[in] toEncoding The desired output encoding.The desired output encoding. May be any of the
* output encodings accepted by omrstr_convert().
*
* @return Number of bytes written from buf, negative portable error code on failure. This may not
* be the number of bytes actually written to the file, which is locale specific.
*
* @note If an error occurred, the file position is unknown.
*/
intptr_t
omrfilestream_write_text(struct OMRPortLibrary *portLibrary, OMRFileStream *fileStream, const char *buf, intptr_t nbytes, int32_t toEncoding)
{
intptr_t rc = 0;
#if defined(CRLFNEWLINES)
uintptr_t i = 0;
uintptr_t j = 0;
uintptr_t newlineCount = 0;
char stackNewlineBuffer[512];
char *newlineBuffer = stackNewlineBuffer;
#endif /* defined(CRLFNEWLINES) */
/* The buffer must be MUTF8 */
int32_t fromEncoding = J9STR_CODE_MUTF8;
Trc_PRT_filestream_write_text_Entry(fileStream, buf, nbytes, toEncoding);
if ((nbytes < 0) || (NULL == buf) || (NULL == fileStream)) {
Trc_PRT_filestream_write_text_invalidArgs(fileStream, buf, nbytes, toEncoding);
Trc_PRT_filestream_write_text_Exit(OMRPORT_ERROR_FILE_INVAL);
return OMRPORT_ERROR_FILE_INVAL;
} else if (0 == nbytes) {
Trc_PRT_filestream_write_text_Exit(0);
return 0;
}
#if defined(CRLFNEWLINES)
/* We have to change the line endings. This must be done entirely before transliteration. */
/* Count the newlines */
for (i = 0; i < ((uintptr_t) nbytes); i++) {
if (buf[i] == '\n') {
newlineCount += 1;
}
}
if (0 < newlineCount) {
/* Every newline now requires 2 bytes */
uintptr_t bufferSize = ((uintptr_t) nbytes) + newlineCount;
/* malloc a bigger buffer if it is needed */
if (bufferSize > sizeof(stackNewlineBuffer)) {
newlineBuffer = portLibrary->mem_allocate_memory(portLibrary, bufferSize, OMR_GET_CALLSITE(), OMRMEM_CATEGORY_PORT_LIBRARY);
if (NULL == newlineBuffer) {
Trc_PRT_filestream_write_text_failedMalloc(fileStream, buf, nbytes, rc);
Trc_PRT_filestream_write_text_Exit(OMRPORT_ERROR_FILE_OPFAILED);
return OMRPORT_ERROR_FILE_OPFAILED;
}
}
/* replace the newlines */
for (i = 0, j = 0; i < bufferSize; i++, j++) {
if (buf[j] == '\n') {
newlineBuffer[i] = '\r';
i += 1;
}
newlineBuffer[i] = buf[j];
}
buf = newlineBuffer;
nbytes = bufferSize;
}
#endif /* defined(CRLFNEWLINES) */
if (fromEncoding == toEncoding) {
/* omrstr_convert() does not support MUTF-8 -> MUTF-8 conversion, so do not use it.
* This has the side effect of not validating the string */
rc = write_all(portLibrary, fileStream, buf, nbytes);
} else {
rc = transliterate_write_text(portLibrary, fileStream, buf, nbytes, toEncoding);
}
#if defined(CRLFNEWLINES)
/* free the buffer only if we had to malloc one, otherwise it is on the stack */
if (newlineBuffer != stackNewlineBuffer) {
portLibrary->mem_free_memory(portLibrary, newlineBuffer);
}
#endif /* defined(CRLFNEWLINES) */
/* if rc is not an error code, then we assume we have written everything from the buffer. At
* this point, we want to return the number of characters consumed from the input buffer, not the
* bytes output to the file.
*/
if (rc > 0) {
rc = nbytes;
}
Trc_PRT_filestream_write_text_Exit(rc);
return rc;
}
/**
* @internal
* Write text to a file by transliterating it.
* No parameter error checking.
*/
static intptr_t
transliterate_write_text(struct OMRPortLibrary *portLibrary, OMRFileStream *fileStream, const char *buf, intptr_t nbytes, int32_t toEncoding)
{
char stackExpandedBuffer[512];
char *expandedBuffer = stackExpandedBuffer;
uintptr_t convertedSize = 0;
intptr_t rc = -1;
/* The buffer must be MUTF8 */
int32_t fromEncoding = J9STR_CODE_MUTF8;
/* Calculate the size of the buffer needed for the converted string.
* If our buffer is not big enough, allocate a new one.
*/
rc = portLibrary->str_convert(portLibrary, fromEncoding, toEncoding, buf, nbytes, NULL, 0);
if (rc < 0) {
Trc_PRT_filestream_write_text_failedStringTransliteration(fileStream, buf, nbytes, toEncoding, (int32_t) rc);
return OMRPORT_ERROR_FILE_OPFAILED;
} else if ((size_t) rc > sizeof(stackExpandedBuffer)) {
expandedBuffer = portLibrary->mem_allocate_memory(portLibrary, rc, OMR_GET_CALLSITE(), OMRMEM_CATEGORY_PORT_LIBRARY);
if (NULL == expandedBuffer) {
Trc_PRT_filestream_write_text_failedMalloc(fileStream, buf, nbytes, rc);
return OMRPORT_ERROR_FILE_OPFAILED;
}
}
convertedSize = rc;
/* Fill the buffer with the new string */
rc = portLibrary->str_convert(portLibrary, fromEncoding, toEncoding, buf, nbytes, expandedBuffer, convertedSize);
if (rc < 0) {
Trc_PRT_filestream_write_text_failedStringTransliteration(fileStream, buf, nbytes, toEncoding, (int32_t) rc);
rc = OMRPORT_ERROR_FILE_OPFAILED;
} else {
/* Write the converted bytes to the file, we will return whatever error/bytecount */
convertedSize = rc;
rc = write_all(portLibrary, fileStream, expandedBuffer, convertedSize);
if (rc < 0) {
Trc_PRT_filestream_write_text_failedToWrite(fileStream, expandedBuffer, convertedSize, rc);
}
}
/* free the buffer only if we had to malloc one, otherwise it is on the stack */
if (stackExpandedBuffer != expandedBuffer) {
portLibrary->mem_free_memory(portLibrary, expandedBuffer);
}
return rc;
}
/**
* @internal
* Write all text to a filestream. This will write in a loop to ensure that all text is written.
*/
static intptr_t
write_all(struct OMRPortLibrary *portLibrary, OMRFileStream *fileStream, const char *buf, intptr_t nbytes)
{
const char *from = buf;
intptr_t remaining = nbytes;
intptr_t rc = 0;
intptr_t result = nbytes;
while (remaining > 0) {
rc = portLibrary->filestream_write(portLibrary, fileStream, from, remaining);
if (rc < 0) {
result = rc;
break;
}
remaining -= rc;
from += rc;
}
return result;
}