1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 ******************************************************************************
5 *
6 * Copyright (C) 1998-2015, International Business Machines
7 * Corporation and others. All Rights Reserved.
8 *
9 ******************************************************************************
10 *
11 * File ufile.cpp
12 *
13 * Modification History:
14 *
15 * Date Name Description
16 * 11/19/98 stephen Creation.
17 * 03/12/99 stephen Modified for new C API.
18 * 06/16/99 stephen Changed T_LocaleBundle to u_locbund
19 * 07/19/99 stephen Fixed to use ucnv's default codepage.
20 ******************************************************************************
21 */
22
23 #include "unicode/platform.h"
24 #if defined(__GNUC__) && !defined(__clang__) && defined(__STRICT_ANSI__)
25 // g++, fileno isn't defined if __STRICT_ANSI__ is defined.
26 // clang fails to compile the <string> header unless __STRICT_ANSI__ is defined.
27 // __GNUC__ is set by both gcc and clang.
28 #undef __STRICT_ANSI__
29 #endif
30
31 #include "locmap.h"
32 #include "unicode/ustdio.h"
33
34 #if !UCONFIG_NO_CONVERSION
35
36 #include <stdlib.h>
37
38 #include "ufile.h"
39 #include "unicode/uloc.h"
40 #include "unicode/ures.h"
41 #include "unicode/ucnv.h"
42 #include "unicode/ustring.h"
43 #include "unicode/unistr.h"
44 #include "cstring.h"
45 #include "cmemory.h"
46
47 #if U_PLATFORM_USES_ONLY_WIN32_API && !defined(fileno)
48 /* Windows likes to rename Unix-like functions */
49 #define fileno _fileno
50 #endif
51
52 static UFILE*
finit_owner(FILE * f,const char * locale,const char * codepage,UBool takeOwnership)53 finit_owner(FILE *f,
54 const char *locale,
55 const char *codepage,
56 UBool takeOwnership
57 )
58 {
59 UErrorCode status = U_ZERO_ERROR;
60 UFILE *result;
61 if(f == NULL) {
62 return 0;
63 }
64 result = (UFILE*) uprv_malloc(sizeof(UFILE));
65 if(result == NULL) {
66 return 0;
67 }
68
69 uprv_memset(result, 0, sizeof(UFILE));
70 result->fFileno = fileno(f);
71 result->fFile = f;
72
73 result->str.fBuffer = result->fUCBuffer;
74 result->str.fPos = result->fUCBuffer;
75 result->str.fLimit = result->fUCBuffer;
76
77 #if !UCONFIG_NO_FORMATTING
78 /* if locale is 0, use the default */
79 if(u_locbund_init(&result->str.fBundle, locale) == 0) {
80 /* DO NOT FCLOSE HERE! */
81 uprv_free(result);
82 return 0;
83 }
84 #endif
85
86 /* If the codepage is not "" use the ucnv_open default behavior */
87 if(codepage == NULL || *codepage != '\0') {
88 result->fConverter = ucnv_open(codepage, &status);
89 }
90 /* else result->fConverter is already memset'd to NULL. */
91
92 if(U_SUCCESS(status)) {
93 result->fOwnFile = takeOwnership;
94 }
95 else {
96 #if !UCONFIG_NO_FORMATTING
97 u_locbund_close(&result->str.fBundle);
98 #endif
99 /* DO NOT fclose here!!!!!! */
100 uprv_free(result);
101 result = NULL;
102 }
103
104 return result;
105 }
106
107 U_CAPI UFILE* U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_finit(FILE * f,const char * locale,const char * codepage)108 u_finit(FILE *f,
109 const char *locale,
110 const char *codepage)
111 {
112 return finit_owner(f, locale, codepage, FALSE);
113 }
114
115 U_CAPI UFILE* U_EXPORT2
u_fadopt(FILE * f,const char * locale,const char * codepage)116 u_fadopt(FILE *f,
117 const char *locale,
118 const char *codepage)
119 {
120 return finit_owner(f, locale, codepage, TRUE);
121 }
122
123 U_CAPI UFILE* U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fopen(const char * filename,const char * perm,const char * locale,const char * codepage)124 u_fopen(const char *filename,
125 const char *perm,
126 const char *locale,
127 const char *codepage)
128 {
129 UFILE *result;
130 FILE *systemFile = fopen(filename, perm);
131 if(systemFile == 0) {
132 return 0;
133 }
134
135 result = finit_owner(systemFile, locale, codepage, TRUE);
136
137 if (!result) {
138 /* Something bad happened.
139 Maybe the converter couldn't be opened. */
140 fclose(systemFile);
141 }
142
143 return result; /* not a file leak */
144 }
145
146 // FILENAME_BUF_MAX represents the largest size that we are willing to use for a
147 // stack-allocated buffer to contain a file name or path. If PATH_MAX (POSIX) or MAX_PATH
148 // (Windows) are defined and are smaller than this we will use their defined value;
149 // otherwise, we will use FILENAME_BUF_MAX for the stack-allocated buffer, and dynamically
150 // allocate a buffer for any file name or path that is that length or longer.
151 #define FILENAME_BUF_MAX 296
152 #if defined PATH_MAX && PATH_MAX < FILENAME_BUF_MAX
153 #define FILENAME_BUF_CAPACITY PATH_MAX
154 #elif defined MAX_PATH && MAX_PATH < FILENAME_BUF_MAX
155 #define FILENAME_BUF_CAPACITY MAX_PATH
156 #else
157 #define FILENAME_BUF_CAPACITY FILENAME_BUF_MAX
158 #endif
159
160 U_CAPI UFILE* U_EXPORT2
u_fopen_u(const UChar * filename,const char * perm,const char * locale,const char * codepage)161 u_fopen_u(const UChar *filename,
162 const char *perm,
163 const char *locale,
164 const char *codepage)
165 {
166 UFILE *result;
167 char buffer[FILENAME_BUF_CAPACITY];
168 char *filenameBuffer = buffer;
169
170 icu::UnicodeString filenameString(true, filename, -1); // readonly aliasing, does not allocate memory
171 // extract with conversion to platform default codepage, return full length (not including 0 termination)
172 int32_t filenameLength = filenameString.extract(0, filenameString.length(), filenameBuffer, FILENAME_BUF_CAPACITY);
173 if (filenameLength >= FILENAME_BUF_CAPACITY) { // could not fit (with zero termination) in buffer
174 filenameBuffer = static_cast<char *>(uprv_malloc(++filenameLength)); // add one for zero termination
175 if (!filenameBuffer) {
176 return nullptr;
177 }
178 filenameString.extract(0, filenameString.length(), filenameBuffer, filenameLength);
179 }
180
181 result = u_fopen(filenameBuffer, perm, locale, codepage);
182 #if U_PLATFORM_USES_ONLY_WIN32_API
183 /* Try Windows API _wfopen if the above fails. */
184 if (!result) {
185 // TODO: test this code path, including wperm.
186 wchar_t wperm[40] = {};
187 size_t retVal;
188 mbstowcs_s(&retVal, wperm, UPRV_LENGTHOF(wperm), perm, _TRUNCATE);
189 FILE *systemFile = _wfopen(reinterpret_cast<const wchar_t *>(filename), wperm); // may return NULL for long filename
190 if (systemFile) {
191 result = finit_owner(systemFile, locale, codepage, TRUE);
192 }
193 if (!result && systemFile) {
194 /* Something bad happened.
195 Maybe the converter couldn't be opened.
196 Bu do not fclose(systemFile) if systemFile is NULL. */
197 fclose(systemFile);
198 }
199 }
200 #endif
201 if (filenameBuffer != buffer) {
202 uprv_free(filenameBuffer);
203 }
204 return result; /* not a file leak */
205 }
206
207
208 U_CAPI UFILE* U_EXPORT2
u_fstropen(UChar * stringBuf,int32_t capacity,const char * locale)209 u_fstropen(UChar *stringBuf,
210 int32_t capacity,
211 const char *locale)
212 {
213 UFILE *result;
214
215 if (capacity < 0) {
216 return NULL;
217 }
218
219 result = (UFILE*) uprv_malloc(sizeof(UFILE));
220 /* Null pointer test */
221 if (result == NULL) {
222 return NULL; /* Just get out. */
223 }
224 uprv_memset(result, 0, sizeof(UFILE));
225 result->str.fBuffer = stringBuf;
226 result->str.fPos = stringBuf;
227 result->str.fLimit = stringBuf+capacity;
228
229 #if !UCONFIG_NO_FORMATTING
230 /* if locale is 0, use the default */
231 if(u_locbund_init(&result->str.fBundle, locale) == 0) {
232 /* DO NOT FCLOSE HERE! */
233 uprv_free(result);
234 return 0;
235 }
236 #endif
237
238 return result;
239 }
240
241 U_CAPI UBool U_EXPORT2
u_feof(UFILE * f)242 u_feof(UFILE *f)
243 {
244 UBool endOfBuffer;
245 if (f == NULL) {
246 return TRUE;
247 }
248 endOfBuffer = (UBool)(f->str.fPos >= f->str.fLimit);
249 if (f->fFile != NULL) {
250 return endOfBuffer && feof(f->fFile);
251 }
252 return endOfBuffer;
253 }
254
255 U_CAPI void U_EXPORT2
u_fflush(UFILE * file)256 u_fflush(UFILE *file)
257 {
258 ufile_flush_translit(file);
259 ufile_flush_io(file);
260 if (file->fFile) {
261 fflush(file->fFile);
262 }
263 else if (file->str.fPos < file->str.fLimit) {
264 *(file->str.fPos++) = 0;
265 }
266 /* TODO: flush input */
267 }
268
269 U_CAPI void
u_frewind(UFILE * file)270 u_frewind(UFILE *file)
271 {
272 u_fflush(file);
273 ucnv_reset(file->fConverter);
274 if (file->fFile) {
275 rewind(file->fFile);
276 file->str.fLimit = file->fUCBuffer;
277 file->str.fPos = file->fUCBuffer;
278 }
279 else {
280 file->str.fPos = file->str.fBuffer;
281 }
282 }
283
284 U_CAPI void U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fclose(UFILE * file)285 u_fclose(UFILE *file)
286 {
287 if (file) {
288 u_fflush(file);
289 ufile_close_translit(file);
290
291 if(file->fOwnFile)
292 fclose(file->fFile);
293
294 #if !UCONFIG_NO_FORMATTING
295 u_locbund_close(&file->str.fBundle);
296 #endif
297
298 ucnv_close(file->fConverter);
299 uprv_free(file);
300 }
301 }
302
303 U_CAPI FILE* U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fgetfile(UFILE * f)304 u_fgetfile( UFILE *f)
305 {
306 return f->fFile;
307 }
308
309 #if !UCONFIG_NO_FORMATTING
310
311 U_CAPI const char* U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fgetlocale(UFILE * file)312 u_fgetlocale( UFILE *file)
313 {
314 return file->str.fBundle.fLocale;
315 }
316
317 U_CAPI int32_t U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fsetlocale(UFILE * file,const char * locale)318 u_fsetlocale(UFILE *file,
319 const char *locale)
320 {
321 u_locbund_close(&file->str.fBundle);
322
323 return u_locbund_init(&file->str.fBundle, locale) == 0 ? -1 : 0;
324 }
325
326 #endif
327
328 U_CAPI const char* U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fgetcodepage(UFILE * file)329 u_fgetcodepage(UFILE *file)
330 {
331 UErrorCode status = U_ZERO_ERROR;
332 const char *codepage = NULL;
333
334 if (file->fConverter) {
335 codepage = ucnv_getName(file->fConverter, &status);
336 if(U_FAILURE(status))
337 return 0;
338 }
339 return codepage;
340 }
341
342 U_CAPI int32_t U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fsetcodepage(const char * codepage,UFILE * file)343 u_fsetcodepage( const char *codepage,
344 UFILE *file)
345 {
346 UErrorCode status = U_ZERO_ERROR;
347 int32_t retVal = -1;
348
349 /* We use the normal default codepage for this system, and not the one for the locale. */
350 if ((file->str.fPos == file->str.fBuffer) && (file->str.fLimit == file->str.fBuffer)) {
351 ucnv_close(file->fConverter);
352 file->fConverter = ucnv_open(codepage, &status);
353 if(U_SUCCESS(status)) {
354 retVal = 0;
355 }
356 }
357 return retVal;
358 }
359
360
361 U_CAPI UConverter * U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fgetConverter(UFILE * file)362 u_fgetConverter(UFILE *file)
363 {
364 return file->fConverter;
365 }
366 #if !UCONFIG_NO_FORMATTING
u_fgetNumberFormat(UFILE * file)367 U_CAPI const UNumberFormat* U_EXPORT2 u_fgetNumberFormat(UFILE *file)
368 {
369 return u_locbund_getNumberFormat(&file->str.fBundle, UNUM_DECIMAL);
370 }
371 #endif
372
373 #endif
374