ufile.cpp 11 KB

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