• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 U_PLATFORM == U_PF_CYGWIN && defined(__STRICT_ANSI__)
25 /* GCC on cygwin (not msys2) with -std=c++11 or newer has stopped defining fileno,
26    unless gcc extensions are enabled (-std=gnu11).
27    fileno is POSIX, but is not standard ANSI C.
28    It has always been a GCC extension, which everyone used until recently.
29    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40278#c7
30 
31    For cygwin/mingw, the FILE* pointer isn't opaque, so we can just use a simple macro.
32    Suggested fix from: https://github.com/gabime/spdlog/issues/1581#issuecomment-650323251
33 */
34 #define _fileno(__F) ((__F)->_file)
35 #define fileno(__F) _fileno(__F)
36 #endif
37 
38 #include "locmap.h"
39 #include "unicode/ustdio.h"
40 
41 #if !UCONFIG_NO_CONVERSION
42 
43 #include <stdlib.h>
44 
45 #include "ufile.h"
46 #include "unicode/uloc.h"
47 #include "unicode/ures.h"
48 #include "unicode/ucnv.h"
49 #include "unicode/ustring.h"
50 #include "unicode/unistr.h"
51 #include "cstring.h"
52 #include "cmemory.h"
53 
54 #if U_PLATFORM_USES_ONLY_WIN32_API && !defined(fileno)
55 /* We will just create an alias to Microsoft's implementation,
56    which is prefixed with _ as they deprecated non-ansi-standard POSIX function names.
57    https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/posix-fileno?view=msvc-170
58 */
59 #define fileno _fileno
60 #endif
61 
62 static UFILE*
finit_owner(FILE * f,const char * locale,const char * codepage,UBool takeOwnership)63 finit_owner(FILE         *f,
64               const char *locale,
65               const char *codepage,
66               UBool       takeOwnership
67               )
68 {
69     UErrorCode status = U_ZERO_ERROR;
70     UFILE     *result;
71     if(f == NULL) {
72         return 0;
73     }
74     result = (UFILE*) uprv_malloc(sizeof(UFILE));
75     if(result == NULL) {
76         return 0;
77     }
78 
79     uprv_memset(result, 0, sizeof(UFILE));
80     result->fFileno = fileno(f);
81     result->fFile = f;
82 
83     result->str.fBuffer = result->fUCBuffer;
84     result->str.fPos    = result->fUCBuffer;
85     result->str.fLimit  = result->fUCBuffer;
86 
87 #if !UCONFIG_NO_FORMATTING
88         /* if locale is 0, use the default */
89         if(u_locbund_init(&result->str.fBundle, locale) == 0) {
90             /* DO NOT FCLOSE HERE! */
91             uprv_free(result);
92             return 0;
93         }
94 #endif
95 
96     /* If the codepage is not "" use the ucnv_open default behavior */
97     if(codepage == NULL || *codepage != '\0') {
98         result->fConverter = ucnv_open(codepage, &status);
99     }
100     /* else result->fConverter is already memset'd to NULL. */
101 
102     if(U_SUCCESS(status)) {
103         result->fOwnFile = takeOwnership;
104     }
105     else {
106 #if !UCONFIG_NO_FORMATTING
107         u_locbund_close(&result->str.fBundle);
108 #endif
109         /* DO NOT fclose here!!!!!! */
110         uprv_free(result);
111         result = NULL;
112     }
113 
114     return result;
115 }
116 
117 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)118 u_finit(FILE          *f,
119         const char    *locale,
120         const char    *codepage)
121 {
122     return finit_owner(f, locale, codepage, false);
123 }
124 
125 U_CAPI UFILE* U_EXPORT2
u_fadopt(FILE * f,const char * locale,const char * codepage)126 u_fadopt(FILE         *f,
127         const char    *locale,
128         const char    *codepage)
129 {
130     return finit_owner(f, locale, codepage, true);
131 }
132 
133 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)134 u_fopen(const char    *filename,
135         const char    *perm,
136         const char    *locale,
137         const char    *codepage)
138 {
139     UFILE     *result;
140     FILE     *systemFile = fopen(filename, perm);
141     if(systemFile == 0) {
142         return 0;
143     }
144 
145     result = finit_owner(systemFile, locale, codepage, true);
146 
147     if (!result) {
148         /* Something bad happened.
149            Maybe the converter couldn't be opened. */
150         fclose(systemFile);
151     }
152 
153     return result; /* not a file leak */
154 }
155 
156 // FILENAME_BUF_MAX represents the largest size that we are willing to use for a
157 // stack-allocated buffer to contain a file name or path. If PATH_MAX (POSIX) or MAX_PATH
158 // (Windows) are defined and are smaller than this we will use their defined value;
159 // otherwise, we will use FILENAME_BUF_MAX for the stack-allocated buffer, and dynamically
160 // allocate a buffer for any file name or path that is that length or longer.
161 #define FILENAME_BUF_MAX 296
162 #if defined PATH_MAX && PATH_MAX < FILENAME_BUF_MAX
163 #define FILENAME_BUF_CAPACITY PATH_MAX
164 #elif defined MAX_PATH && MAX_PATH < FILENAME_BUF_MAX
165 #define FILENAME_BUF_CAPACITY MAX_PATH
166 #else
167 #define FILENAME_BUF_CAPACITY FILENAME_BUF_MAX
168 #endif
169 
170 U_CAPI UFILE* U_EXPORT2
u_fopen_u(const UChar * filename,const char * perm,const char * locale,const char * codepage)171 u_fopen_u(const UChar   *filename,
172         const char    *perm,
173         const char    *locale,
174         const char    *codepage)
175 {
176     UFILE *result;
177     char buffer[FILENAME_BUF_CAPACITY];
178     char *filenameBuffer = buffer;
179 
180     icu::UnicodeString filenameString(true, filename, -1); // readonly aliasing, does not allocate memory
181     // extract with conversion to platform default codepage, return full length (not including 0 termination)
182     int32_t filenameLength = filenameString.extract(0, filenameString.length(), filenameBuffer, FILENAME_BUF_CAPACITY);
183     if (filenameLength >= FILENAME_BUF_CAPACITY) { // could not fit (with zero termination) in buffer
184         filenameBuffer = static_cast<char *>(uprv_malloc(++filenameLength)); // add one for zero termination
185         if (!filenameBuffer) {
186             return nullptr;
187         }
188         filenameString.extract(0, filenameString.length(), filenameBuffer, filenameLength);
189     }
190 
191     result = u_fopen(filenameBuffer, perm, locale, codepage);
192 #if U_PLATFORM_USES_ONLY_WIN32_API
193     /* Try Windows API _wfopen if the above fails. */
194     if (!result) {
195         // TODO: test this code path, including wperm.
196         wchar_t wperm[40] = {};
197         size_t  retVal;
198         mbstowcs_s(&retVal, wperm, UPRV_LENGTHOF(wperm), perm, _TRUNCATE);
199         FILE *systemFile = _wfopen(reinterpret_cast<const wchar_t *>(filename), wperm); // may return NULL for long filename
200         if (systemFile) {
201             result = finit_owner(systemFile, locale, codepage, true);
202         }
203         if (!result && systemFile) {
204             /* Something bad happened.
205                Maybe the converter couldn't be opened.
206                Bu do not fclose(systemFile) if systemFile is NULL. */
207             fclose(systemFile);
208         }
209     }
210 #endif
211     if (filenameBuffer != buffer) {
212         uprv_free(filenameBuffer);
213     }
214     return result; /* not a file leak */
215 }
216 
217 
218 U_CAPI UFILE* U_EXPORT2
u_fstropen(UChar * stringBuf,int32_t capacity,const char * locale)219 u_fstropen(UChar *stringBuf,
220            int32_t      capacity,
221            const char  *locale)
222 {
223     UFILE *result;
224 
225     if (capacity < 0) {
226         return NULL;
227     }
228 
229     result = (UFILE*) uprv_malloc(sizeof(UFILE));
230     /* Null pointer test */
231     if (result == NULL) {
232         return NULL; /* Just get out. */
233     }
234     uprv_memset(result, 0, sizeof(UFILE));
235     result->str.fBuffer = stringBuf;
236     result->str.fPos    = stringBuf;
237     result->str.fLimit  = stringBuf+capacity;
238 
239 #if !UCONFIG_NO_FORMATTING
240     /* if locale is 0, use the default */
241     if(u_locbund_init(&result->str.fBundle, locale) == 0) {
242         /* DO NOT FCLOSE HERE! */
243         uprv_free(result);
244         return 0;
245     }
246 #endif
247 
248     return result;
249 }
250 
251 U_CAPI UBool U_EXPORT2
u_feof(UFILE * f)252 u_feof(UFILE  *f)
253 {
254     UBool endOfBuffer;
255     if (f == NULL) {
256         return true;
257     }
258     endOfBuffer = (UBool)(f->str.fPos >= f->str.fLimit);
259     if (f->fFile != NULL) {
260         return endOfBuffer && feof(f->fFile);
261     }
262     return endOfBuffer;
263 }
264 
265 U_CAPI void U_EXPORT2
u_fflush(UFILE * file)266 u_fflush(UFILE *file)
267 {
268     ufile_flush_translit(file);
269     ufile_flush_io(file);
270     if (file->fFile) {
271         fflush(file->fFile);
272     }
273     else if (file->str.fPos < file->str.fLimit) {
274         *(file->str.fPos++) = 0;
275     }
276     /* TODO: flush input */
277 }
278 
279 U_CAPI void
u_frewind(UFILE * file)280 u_frewind(UFILE *file)
281 {
282     u_fflush(file);
283     ucnv_reset(file->fConverter);
284     if (file->fFile) {
285         rewind(file->fFile);
286         file->str.fLimit = file->fUCBuffer;
287         file->str.fPos   = file->fUCBuffer;
288     }
289     else {
290         file->str.fPos = file->str.fBuffer;
291     }
292 }
293 
294 U_CAPI void U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fclose(UFILE * file)295 u_fclose(UFILE *file)
296 {
297     if (file) {
298         u_fflush(file);
299         ufile_close_translit(file);
300 
301         if(file->fOwnFile)
302             fclose(file->fFile);
303 
304 #if !UCONFIG_NO_FORMATTING
305         u_locbund_close(&file->str.fBundle);
306 #endif
307 
308         ucnv_close(file->fConverter);
309         uprv_free(file);
310     }
311 }
312 
313 U_CAPI FILE* U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fgetfile(UFILE * f)314 u_fgetfile(    UFILE         *f)
315 {
316     return f->fFile;
317 }
318 
319 #if !UCONFIG_NO_FORMATTING
320 
321 U_CAPI const char*  U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fgetlocale(UFILE * file)322 u_fgetlocale(    UFILE        *file)
323 {
324     return file->str.fBundle.fLocale;
325 }
326 
327 U_CAPI int32_t U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fsetlocale(UFILE * file,const char * locale)328 u_fsetlocale(UFILE      *file,
329              const char *locale)
330 {
331     u_locbund_close(&file->str.fBundle);
332 
333     return u_locbund_init(&file->str.fBundle, locale) == 0 ? -1 : 0;
334 }
335 
336 #endif
337 
338 U_CAPI const char* U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fgetcodepage(UFILE * file)339 u_fgetcodepage(UFILE        *file)
340 {
341     UErrorCode     status = U_ZERO_ERROR;
342     const char     *codepage = NULL;
343 
344     if (file->fConverter) {
345         codepage = ucnv_getName(file->fConverter, &status);
346         if(U_FAILURE(status))
347             return 0;
348     }
349     return codepage;
350 }
351 
352 U_CAPI int32_t U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fsetcodepage(const char * codepage,UFILE * file)353 u_fsetcodepage(    const char    *codepage,
354                UFILE        *file)
355 {
356     UErrorCode status = U_ZERO_ERROR;
357     int32_t retVal = -1;
358 
359     /* We use the normal default codepage for this system, and not the one for the locale. */
360     if ((file->str.fPos == file->str.fBuffer) && (file->str.fLimit == file->str.fBuffer)) {
361         ucnv_close(file->fConverter);
362         file->fConverter = ucnv_open(codepage, &status);
363         if(U_SUCCESS(status)) {
364             retVal = 0;
365         }
366     }
367     return retVal;
368 }
369 
370 
371 U_CAPI UConverter * U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_fgetConverter(UFILE * file)372 u_fgetConverter(UFILE *file)
373 {
374     return file->fConverter;
375 }
376 #if !UCONFIG_NO_FORMATTING
u_fgetNumberFormat(UFILE * file)377 U_CAPI const UNumberFormat* U_EXPORT2 u_fgetNumberFormat(UFILE *file)
378 {
379     return u_locbund_getNumberFormat(&file->str.fBundle, UNUM_DECIMAL);
380 }
381 #endif
382 
383 #endif
384