sqlite3_stdio.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /*
  2. ** 2024-09-24
  3. **
  4. ** The author disclaims copyright to this source code. In place of
  5. ** a legal notice, here is a blessing:
  6. **
  7. ** May you do good and not evil.
  8. ** May you find forgiveness for yourself and forgive others.
  9. ** May you share freely, never taking more than you give.
  10. **
  11. *************************************************************************
  12. **
  13. ** Implementation of standard I/O interfaces for UTF-8 that are missing
  14. ** on Windows.
  15. */
  16. #ifdef _WIN32 /* This file is a no-op on all platforms except Windows */
  17. #ifndef _SQLITE3_STDIO_H_
  18. #include "sqlite3_stdio.h"
  19. #endif
  20. #undef WIN32_LEAN_AND_MEAN
  21. #define WIN32_LEAN_AND_MEAN
  22. #include <windows.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <stdio.h>
  26. #include <assert.h>
  27. #include "sqlite3.h"
  28. #include <ctype.h>
  29. #include <stdarg.h>
  30. #include <io.h>
  31. #include <fcntl.h>
  32. /*
  33. ** If the SQLITE_U8TEXT_ONLY option is defined, then use O_U8TEXT
  34. ** when appropriate on all output. (Sometimes use O_BINARY when
  35. ** rendering ASCII text in cases where NL-to-CRLF expansion would
  36. ** not be correct.)
  37. **
  38. ** If the SQLITE_U8TEXT_STDIO option is defined, then use O_U8TEXT
  39. ** when appropriate when writing to stdout or stderr. Use O_BINARY
  40. ** or O_TEXT (depending on things like the .mode and the .crlf setting
  41. ** in the CLI, or other context clues in other applications) for all
  42. ** other output channels.
  43. **
  44. ** The default behavior, if neither of the above is defined is to
  45. ** use O_U8TEXT when writing to the Windows console (or anything
  46. ** else for which _isatty() returns true) and to use O_BINARY or O_TEXT
  47. ** for all other output channels.
  48. */
  49. #if defined(SQLITE_U8TEXT_ONLY)
  50. # define UseWtextForOutput(fd) 1
  51. # define UseWtextForInput(fd) 1
  52. # define IsConsole(fd) _isatty(_fileno(fd))
  53. #elif defined(SQLITE_U8TEXT_STDIO)
  54. # define UseWtextForOutput(fd) ((fd)==stdout || (fd)==stderr)
  55. # define UseWtextForInput(fd) ((fd)==stdin)
  56. # define IsConsole(fd) _isatty(_fileno(fd))
  57. #else
  58. # define UseWtextForOutput(fd) _isatty(_fileno(fd))
  59. # define UseWtextForInput(fd) _isatty(_fileno(fd))
  60. # define IsConsole(fd) 1
  61. #endif
  62. /*
  63. ** Global variables determine if simulated O_BINARY mode is to be
  64. ** used for stdout or other, respectively. Simulated O_BINARY mode
  65. ** means the mode is usually O_BINARY, but switches to O_U8TEXT for
  66. ** unicode characters U+0080 or greater (any character that has a
  67. ** multi-byte representation in UTF-8). This is the only way we
  68. ** have found to render Unicode characters on a Windows console while
  69. ** at the same time avoiding undesirable \n to \r\n translation.
  70. */
  71. static int simBinaryStdout = 0;
  72. static int simBinaryOther = 0;
  73. /*
  74. ** Determine if simulated binary mode should be used for output to fd
  75. */
  76. static int UseBinaryWText(FILE *fd){
  77. if( fd==stdout || fd==stderr ){
  78. return simBinaryStdout;
  79. }else{
  80. return simBinaryOther;
  81. }
  82. }
  83. /*
  84. ** Work-alike for the fopen() routine from the standard C library.
  85. */
  86. FILE *sqlite3_fopen(const char *zFilename, const char *zMode){
  87. FILE *fp = 0;
  88. wchar_t *b1, *b2;
  89. int sz1, sz2;
  90. sz1 = (int)strlen(zFilename);
  91. sz2 = (int)strlen(zMode);
  92. b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
  93. b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
  94. if( b1 && b2 ){
  95. sz1 = MultiByteToWideChar(CP_UTF8, 0, zFilename, sz1, b1, sz1);
  96. b1[sz1] = 0;
  97. sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2);
  98. b2[sz2] = 0;
  99. fp = _wfopen(b1, b2);
  100. }
  101. sqlite3_free(b1);
  102. sqlite3_free(b2);
  103. simBinaryOther = 0;
  104. return fp;
  105. }
  106. /*
  107. ** Work-alike for the popen() routine from the standard C library.
  108. */
  109. FILE *sqlite3_popen(const char *zCommand, const char *zMode){
  110. FILE *fp = 0;
  111. wchar_t *b1, *b2;
  112. int sz1, sz2;
  113. sz1 = (int)strlen(zCommand);
  114. sz2 = (int)strlen(zMode);
  115. b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
  116. b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
  117. if( b1 && b2 ){
  118. sz1 = MultiByteToWideChar(CP_UTF8, 0, zCommand, sz1, b1, sz1);
  119. b1[sz1] = 0;
  120. sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2);
  121. b2[sz2] = 0;
  122. fp = _wpopen(b1, b2);
  123. }
  124. sqlite3_free(b1);
  125. sqlite3_free(b2);
  126. return fp;
  127. }
  128. /*
  129. ** Work-alike for fgets() from the standard C library.
  130. */
  131. char *sqlite3_fgets(char *buf, int sz, FILE *in){
  132. if( UseWtextForInput(in) ){
  133. /* When reading from the command-prompt in Windows, it is necessary
  134. ** to use _O_WTEXT input mode to read UTF-16 characters, then translate
  135. ** that into UTF-8. Otherwise, non-ASCII characters all get translated
  136. ** into '?'.
  137. */
  138. wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) );
  139. if( b1==0 ) return 0;
  140. #ifndef SQLITE_USE_STDIO_FOR_CONSOLE
  141. DWORD nRead = 0;
  142. if( IsConsole(in)
  143. && ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), b1, sz, &nRead, 0)
  144. ){
  145. b1[nRead] = 0;
  146. }else
  147. #endif
  148. {
  149. _setmode(_fileno(in), IsConsole(in) ? _O_WTEXT : _O_U8TEXT);
  150. if( fgetws(b1, sz/4, in)==0 ){
  151. sqlite3_free(b1);
  152. return 0;
  153. }
  154. }
  155. WideCharToMultiByte(CP_UTF8, 0, b1, -1, buf, sz, 0, 0);
  156. sqlite3_free(b1);
  157. return buf;
  158. }else{
  159. /* Reading from a file or other input source, just read bytes without
  160. ** any translation. */
  161. return fgets(buf, sz, in);
  162. }
  163. }
  164. /*
  165. ** Send ASCII text as O_BINARY. But for Unicode characters U+0080 and
  166. ** greater, switch to O_U8TEXT.
  167. */
  168. static void piecemealOutput(wchar_t *b1, int sz, FILE *out){
  169. int i;
  170. wchar_t c;
  171. while( sz>0 ){
  172. for(i=0; i<sz && b1[i]>=0x80; i++){}
  173. if( i>0 ){
  174. c = b1[i];
  175. b1[i] = 0;
  176. fflush(out);
  177. _setmode(_fileno(out), _O_U8TEXT);
  178. fputws(b1, out);
  179. fflush(out);
  180. b1 += i;
  181. b1[0] = c;
  182. sz -= i;
  183. }else{
  184. fflush(out);
  185. _setmode(_fileno(out), _O_TEXT);
  186. _setmode(_fileno(out), _O_BINARY);
  187. fwrite(&b1[0], 1, 1, out);
  188. for(i=1; i<sz && b1[i]<0x80; i++){
  189. fwrite(&b1[i], 1, 1, out);
  190. }
  191. fflush(out);
  192. _setmode(_fileno(out), _O_U8TEXT);
  193. b1 += i;
  194. sz -= i;
  195. }
  196. }
  197. }
  198. /*
  199. ** Work-alike for fputs() from the standard C library.
  200. */
  201. int sqlite3_fputs(const char *z, FILE *out){
  202. if( !UseWtextForOutput(out) ){
  203. /* Writing to a file or other destination, just write bytes without
  204. ** any translation. */
  205. return fputs(z, out);
  206. }else{
  207. /* One must use UTF16 in order to get unicode support when writing
  208. ** to the console on Windows.
  209. */
  210. int sz = (int)strlen(z);
  211. wchar_t *b1 = sqlite3_malloc( (sz+1)*sizeof(wchar_t) );
  212. if( b1==0 ) return 0;
  213. sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz);
  214. b1[sz] = 0;
  215. #ifndef SQLITE_STDIO_FOR_CONSOLE
  216. DWORD nWr = 0;
  217. if( IsConsole(out)
  218. && WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),b1,sz,&nWr,0)
  219. ){
  220. /* If writing to the console, then the WriteConsoleW() is all we
  221. ** need to do. */
  222. }else
  223. #endif
  224. {
  225. /* For non-console I/O, or if SQLITE_USE_STDIO_FOR_CONSOLE is defined
  226. ** then write using the standard library. */
  227. _setmode(_fileno(out), _O_U8TEXT);
  228. if( UseBinaryWText(out) ){
  229. piecemealOutput(b1, sz, out);
  230. }else{
  231. fputws(b1, out);
  232. }
  233. }
  234. sqlite3_free(b1);
  235. return 0;
  236. }
  237. }
  238. /*
  239. ** Work-alike for fprintf() from the standard C library.
  240. */
  241. int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
  242. int rc;
  243. if( UseWtextForOutput(out) ){
  244. /* When writing to the command-prompt in Windows, it is necessary
  245. ** to use _O_WTEXT input mode and write UTF-16 characters.
  246. */
  247. char *z;
  248. va_list ap;
  249. va_start(ap, zFormat);
  250. z = sqlite3_vmprintf(zFormat, ap);
  251. va_end(ap);
  252. sqlite3_fputs(z, out);
  253. rc = (int)strlen(z);
  254. sqlite3_free(z);
  255. }else{
  256. /* Writing to a file or other destination, just write bytes without
  257. ** any translation. */
  258. va_list ap;
  259. va_start(ap, zFormat);
  260. rc = vfprintf(out, zFormat, ap);
  261. va_end(ap);
  262. }
  263. return rc;
  264. }
  265. /*
  266. ** Set the mode for an output stream. mode argument is typically _O_BINARY or
  267. ** _O_TEXT.
  268. */
  269. void sqlite3_fsetmode(FILE *fp, int mode){
  270. if( !UseWtextForOutput(fp) ){
  271. fflush(fp);
  272. _setmode(_fileno(fp), mode);
  273. }else if( fp==stdout || fp==stderr ){
  274. simBinaryStdout = (mode==_O_BINARY);
  275. }else{
  276. simBinaryOther = (mode==_O_BINARY);
  277. }
  278. }
  279. #endif /* defined(_WIN32) */