ufile.cpp (11122B)
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* 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 == nullptr) { 72 return nullptr; 73 } 74 result = static_cast<UFILE*>(uprv_malloc(sizeof(UFILE))); 75 if(result == nullptr) { 76 return nullptr; 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) == nullptr) { 90 /* DO NOT FCLOSE HERE! */ 91 uprv_free(result); 92 return nullptr; 93 } 94 #endif 95 96 /* If the codepage is not "" use the ucnv_open default behavior */ 97 if(codepage == nullptr || *codepage != '\0') { 98 result->fConverter = ucnv_open(codepage, &status); 99 } 100 /* else result->fConverter is already memset'd to nullptr. */ 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 = nullptr; 112 } 113 114 return result; 115 } 116 117 U_CAPI UFILE* U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */ 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 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 */ 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 == nullptr) { 142 return nullptr; 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 171 u_fopen_u(const char16_t *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 nullptr 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 nullptr. */ 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 219 u_fstropen(char16_t *stringBuf, 220 int32_t capacity, 221 const char *locale) 222 { 223 UFILE *result; 224 225 if (capacity < 0) { 226 return nullptr; 227 } 228 229 result = (UFILE*) uprv_malloc(sizeof(UFILE)); 230 /* Null pointer test */ 231 if (result == nullptr) { 232 return nullptr; /* 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) == nullptr) { 242 /* DO NOT FCLOSE HERE! */ 243 uprv_free(result); 244 return nullptr; 245 } 246 #endif 247 248 return result; 249 } 250 251 U_CAPI UBool U_EXPORT2 252 u_feof(UFILE *f) 253 { 254 UBool endOfBuffer; 255 if (f == nullptr) { 256 return true; 257 } 258 endOfBuffer = f->str.fPos >= f->str.fLimit; 259 if (f->fFile != nullptr) { 260 return endOfBuffer && feof(f->fFile); 261 } 262 return endOfBuffer; 263 } 264 265 U_CAPI void U_EXPORT2 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 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 */ 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 */ 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 */ 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 */ 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) == nullptr ? -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 */ 339 u_fgetcodepage(UFILE *file) 340 { 341 UErrorCode status = U_ZERO_ERROR; 342 const char *codepage = nullptr; 343 344 if (file->fConverter) { 345 codepage = ucnv_getName(file->fConverter, &status); 346 if(U_FAILURE(status)) 347 return nullptr; 348 } 349 return codepage; 350 } 351 352 U_CAPI int32_t U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */ 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 */ 372 u_fgetConverter(UFILE *file) 373 { 374 return file->fConverter; 375 } 376 #if !UCONFIG_NO_FORMATTING 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