1// Copyright (C) 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.c
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/*
24 * fileno is not declared when building with GCC in strict mode.
25 */
26#if defined(__GNUC__) && defined(__STRICT_ANSI__)
27#undef __STRICT_ANSI__
28#endif
29
30#include "locmap.h"
31#include "unicode/ustdio.h"
32
33#if !UCONFIG_NO_CONVERSION
34
35#include "ufile.h"
36#include "unicode/uloc.h"
37#include "unicode/ures.h"
38#include "unicode/ucnv.h"
39#include "unicode/ustring.h"
40#include "cstring.h"
41#include "cmemory.h"
42
43#if U_PLATFORM_USES_ONLY_WIN32_API && !defined(fileno)
44/* Windows likes to rename Unix-like functions */
45#define fileno _fileno
46#endif
47
48static UFILE*
49finit_owner(FILE         *f,
50              const char *locale,
51              const char *codepage,
52              UBool       takeOwnership
53              )
54{
55    UErrorCode status = U_ZERO_ERROR;
56    UFILE     *result;
57    if(f == NULL) {
58        return 0;
59    }
60    result = (UFILE*) uprv_malloc(sizeof(UFILE));
61    if(result == NULL) {
62        return 0;
63    }
64
65    uprv_memset(result, 0, sizeof(UFILE));
66    result->fFileno = fileno(f);
67
68#if U_PLATFORM_USES_ONLY_WIN32_API && _MSC_VER < 1900
69    /*
70     * Below is a very old workaround (ICU ticket:231).
71     *
72     * Previously, 'FILE*' from inside and outside ICU's DLL
73     * were different, because they pointed into local copies
74     * of the io block. At least by VS 2015 the implementation
75     * is something like:
76     *    stdio = _acrt_iob_func(0)
77     * .. which is a function call, so should return the same pointer
78     * regardless of call site.
79     * As of _MSC_VER 1900 this patch is retired, at 16 years old.
80     */
81    if (0 <= result->fFileno && result->fFileno <= 2) {
82        /* stdin, stdout and stderr need to be special cased for Windows 98 */
83#if _MSC_VER >= 1400
84        result->fFile = &__iob_func()[_fileno(f)];
85#else
86        result->fFile = &_iob[_fileno(f)];
87#endif
88    }
89    else
90#endif
91    {
92        result->fFile = f;
93    }
94
95    result->str.fBuffer = result->fUCBuffer;
96    result->str.fPos    = result->fUCBuffer;
97    result->str.fLimit  = result->fUCBuffer;
98
99#if !UCONFIG_NO_FORMATTING
100        /* if locale is 0, use the default */
101        if(u_locbund_init(&result->str.fBundle, locale) == 0) {
102            /* DO NOT FCLOSE HERE! */
103            uprv_free(result);
104            return 0;
105        }
106#endif
107
108    /* If the codepage is not "" use the ucnv_open default behavior */
109    if(codepage == NULL || *codepage != '\0') {
110        result->fConverter = ucnv_open(codepage, &status);
111    }
112    /* else result->fConverter is already memset'd to NULL. */
113
114    if(U_SUCCESS(status)) {
115        result->fOwnFile = takeOwnership;
116    }
117    else {
118#if !UCONFIG_NO_FORMATTING
119        u_locbund_close(&result->str.fBundle);
120#endif
121        /* DO NOT fclose here!!!!!! */
122        uprv_free(result);
123        result = NULL;
124    }
125
126    return result;
127}
128
129U_CAPI UFILE* U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
130u_finit(FILE          *f,
131        const char    *locale,
132        const char    *codepage)
133{
134    return finit_owner(f, locale, codepage, FALSE);
135}
136
137U_CAPI UFILE* U_EXPORT2
138u_fadopt(FILE         *f,
139        const char    *locale,
140        const char    *codepage)
141{
142    return finit_owner(f, locale, codepage, TRUE);
143}
144
145U_CAPI UFILE* U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
146u_fopen(const char    *filename,
147        const char    *perm,
148        const char    *locale,
149        const char    *codepage)
150{
151    UFILE     *result;
152    FILE     *systemFile = fopen(filename, perm);
153    if(systemFile == 0) {
154        return 0;
155    }
156
157    result = finit_owner(systemFile, locale, codepage, TRUE);
158
159    if (!result) {
160        /* Something bad happened.
161           Maybe the converter couldn't be opened. */
162        fclose(systemFile);
163    }
164
165    return result; /* not a file leak */
166}
167
168U_CAPI UFILE* U_EXPORT2
169u_fopen_u(const UChar   *filename,
170        const char    *perm,
171        const char    *locale,
172        const char    *codepage)
173{
174    UFILE     *result;
175    char buffer[256];
176
177    u_austrcpy(buffer, filename);
178
179    result = u_fopen(buffer, perm, locale, codepage);
180#if U_PLATFORM_USES_ONLY_WIN32_API
181    /* Try Windows API _wfopen if the above fails. */
182    if (!result) {
183        FILE *systemFile = _wfopen(filename, (UChar*)perm);
184        if (systemFile) {
185            result = finit_owner(systemFile, locale, codepage, TRUE);
186        }
187        if (!result) {
188            /* Something bad happened.
189               Maybe the converter couldn't be opened. */
190            fclose(systemFile);
191        }
192    }
193#endif
194    return result; /* not a file leak */
195}
196
197U_CAPI UFILE* U_EXPORT2
198u_fstropen(UChar *stringBuf,
199           int32_t      capacity,
200           const char  *locale)
201{
202    UFILE *result;
203
204    if (capacity < 0) {
205        return NULL;
206    }
207
208    result = (UFILE*) uprv_malloc(sizeof(UFILE));
209    /* Null pointer test */
210    if (result == NULL) {
211    	return NULL; /* Just get out. */
212    }
213    uprv_memset(result, 0, sizeof(UFILE));
214    result->str.fBuffer = stringBuf;
215    result->str.fPos    = stringBuf;
216    result->str.fLimit  = stringBuf+capacity;
217
218#if !UCONFIG_NO_FORMATTING
219    /* if locale is 0, use the default */
220    if(u_locbund_init(&result->str.fBundle, locale) == 0) {
221        /* DO NOT FCLOSE HERE! */
222        uprv_free(result);
223        return 0;
224    }
225#endif
226
227    return result;
228}
229
230U_CAPI UBool U_EXPORT2
231u_feof(UFILE  *f)
232{
233    UBool endOfBuffer;
234    if (f == NULL) {
235        return TRUE;
236    }
237    endOfBuffer = (UBool)(f->str.fPos >= f->str.fLimit);
238    if (f->fFile != NULL) {
239        return endOfBuffer && feof(f->fFile);
240    }
241    return endOfBuffer;
242}
243
244U_CAPI void U_EXPORT2
245u_fflush(UFILE *file)
246{
247    ufile_flush_translit(file);
248    ufile_flush_io(file);
249    if (file->fFile) {
250        fflush(file->fFile);
251    }
252    else if (file->str.fPos < file->str.fLimit) {
253        *(file->str.fPos++) = 0;
254    }
255    /* TODO: flush input */
256}
257
258U_CAPI void
259u_frewind(UFILE *file)
260{
261    u_fflush(file);
262    ucnv_reset(file->fConverter);
263    if (file->fFile) {
264        rewind(file->fFile);
265        file->str.fLimit = file->fUCBuffer;
266        file->str.fPos   = file->fUCBuffer;
267    }
268    else {
269        file->str.fPos = file->str.fBuffer;
270    }
271}
272
273U_CAPI void U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
274u_fclose(UFILE *file)
275{
276    if (file) {
277        u_fflush(file);
278        ufile_close_translit(file);
279
280        if(file->fOwnFile)
281            fclose(file->fFile);
282
283#if !UCONFIG_NO_FORMATTING
284        u_locbund_close(&file->str.fBundle);
285#endif
286
287        ucnv_close(file->fConverter);
288        uprv_free(file);
289    }
290}
291
292U_CAPI FILE* U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
293u_fgetfile(    UFILE         *f)
294{
295    return f->fFile;
296}
297
298#if !UCONFIG_NO_FORMATTING
299
300U_CAPI const char*  U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
301u_fgetlocale(    UFILE        *file)
302{
303    return file->str.fBundle.fLocale;
304}
305
306U_CAPI int32_t U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
307u_fsetlocale(UFILE      *file,
308             const char *locale)
309{
310    u_locbund_close(&file->str.fBundle);
311
312    return u_locbund_init(&file->str.fBundle, locale) == 0 ? -1 : 0;
313}
314
315#endif
316
317U_CAPI const char* U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
318u_fgetcodepage(UFILE        *file)
319{
320    UErrorCode     status = U_ZERO_ERROR;
321    const char     *codepage = NULL;
322
323    if (file->fConverter) {
324        codepage = ucnv_getName(file->fConverter, &status);
325        if(U_FAILURE(status))
326            return 0;
327    }
328    return codepage;
329}
330
331U_CAPI int32_t U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
332u_fsetcodepage(    const char    *codepage,
333               UFILE        *file)
334{
335    UErrorCode status = U_ZERO_ERROR;
336    int32_t retVal = -1;
337
338    /* We use the normal default codepage for this system, and not the one for the locale. */
339    if ((file->str.fPos == file->str.fBuffer) && (file->str.fLimit == file->str.fBuffer)) {
340        ucnv_close(file->fConverter);
341        file->fConverter = ucnv_open(codepage, &status);
342        if(U_SUCCESS(status)) {
343            retVal = 0;
344        }
345    }
346    return retVal;
347}
348
349
350U_CAPI UConverter * U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
351u_fgetConverter(UFILE *file)
352{
353    return file->fConverter;
354}
355#if !UCONFIG_NO_FORMATTING
356U_CAPI const UNumberFormat* U_EXPORT2 u_fgetNumberFormat(UFILE *file)
357{
358    return u_locbund_getNumberFormat(&file->str.fBundle, UNUM_DECIMAL);
359}
360#endif
361
362#endif
363