stdio_.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. * 文件名: stdio_.c
  3. * 目标: 用于终端输出的控制 (stdout, stdin, stderr)
  4. * 因为不同平台上终端采用的编码不同
  5. */
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <stdarg.h>
  9. #include "tool.h"
  10. #ifdef aFunWIN32_NO_CYGWIN
  11. #ifdef _MSC_VER
  12. #pragma warning(disable : 5105) // 关闭 5105 的警告输出 (Windows.h中使用)
  13. #endif
  14. #include <conio.h>
  15. #include <Windows.h>
  16. // 获取CodePage, 并将内存中utf-8字符串转换为对应编码输出
  17. // cygwin环境下, 终端默认为uft-8
  18. static bool stdin_empty = true; // stdin读取内容遇到 \n, 用于检测stdin是否为空
  19. static int convertMultiByte(char **dest, char *str, UINT from, UINT to) {
  20. if (str == NULL || dest == NULL)
  21. return 0;
  22. int tmp_len = MultiByteToWideChar(from, 0, str, -1, 0, 0);
  23. if (tmp_len == 0)
  24. return 0;
  25. wchar_t *tmp = calloc(tmp_len + 1, sizeof(wchar_t));
  26. if (MultiByteToWideChar(from, 0, str, -1, tmp, tmp_len) == 0)
  27. return 0;
  28. int dest_len = WideCharToMultiByte(to, 0, tmp, -1, NULL, 0, NULL, NULL);
  29. if (dest_len == 0)
  30. return 0;
  31. *dest = calloc(dest_len + 1, sizeof(char));
  32. int re = WideCharToMultiByte(to, 0, tmp, -1, *dest, dest_len, NULL, NULL);
  33. free(tmp);
  34. return re;
  35. }
  36. int fgets_stdin(char **dest, int len) {
  37. char *wstr = calloc(len, sizeof(char));
  38. int re = 0;
  39. UINT code_page = GetConsoleCP();
  40. if (fgets(wstr, len, stdin) != NULL)
  41. re = convertMultiByte(dest, wstr, code_page, CP_UTF8);
  42. if (strchr(wstr, '\n') != NULL)
  43. stdin_empty = true; // 没有读取到\n说明stdin还有内容
  44. else
  45. stdin_empty = false; // 没有读取到\n说明stdin还有内容
  46. return re;
  47. }
  48. int fgetchar_stdin(void) {
  49. int ch = getc(stdin);
  50. if (ch == '\n')
  51. stdin_empty = true;
  52. else
  53. stdin_empty = false;
  54. return ch;
  55. }
  56. int fungetc_stdin(int ch) {
  57. int re = ungetc(ch, stdin);
  58. stdin_empty = false;
  59. return re;
  60. }
  61. /*
  62. * 函数名: checkStdin
  63. * 目标: 检查stdin缓冲区是否有内容
  64. * 有内容则返回true
  65. * 无内容则返回false
  66. */
  67. bool checkStdin(void) {
  68. if (!stdin_empty)
  69. return true;
  70. return _kbhit();
  71. }
  72. static int fputs_std_(char *str, FILE *std) {
  73. UINT code_page = GetConsoleCP();
  74. char *wstr = NULL;
  75. int re = EOF;
  76. convertMultiByte(&wstr, str, CP_UTF8, code_page);
  77. if (wstr != NULL) {
  78. re = fputs(wstr, std);
  79. free(wstr);
  80. }
  81. return re;
  82. }
  83. int fputs_stdout(char *str) {
  84. return fputs_std_(str, stdout);
  85. }
  86. int fputs_stderr(char *str) {
  87. return fputs_std_(str, stderr);
  88. }
  89. static size_t vprintf_std_(FILE *std, size_t buf_len, char *format, va_list ap) {
  90. if (buf_len == 0)
  91. buf_len = 1024;
  92. buf_len += 10; // 预留更多位置
  93. char *buf = calloc(buf_len, sizeof(char));
  94. size_t re = vsnprintf(buf, buf_len, format, ap);
  95. if (fputs_std_(buf, std) == EOF)
  96. re = 0;
  97. free(buf);
  98. return re;
  99. }
  100. size_t vprintf_stdout(size_t buf_len, char *format, va_list ap) {
  101. return vprintf_std_(stdout, buf_len, format, ap);
  102. }
  103. size_t vprintf_stderr(size_t buf_len, char *format, va_list ap) {
  104. return vprintf_std_(stderr, buf_len, format, ap);
  105. }
  106. size_t printf_stdout(size_t buf_len, char *format, ...) {
  107. va_list ap;
  108. va_start(ap, format);
  109. size_t re = vprintf_std_(stdout, buf_len, format, ap);
  110. va_end(ap);
  111. return re;
  112. }
  113. size_t printf_stderr(size_t buf_len, char *format, ...) {
  114. va_list ap;
  115. va_start(ap, format);
  116. size_t re = vprintf_std_(stderr, buf_len, format, ap);
  117. va_end(ap);
  118. return re;
  119. }
  120. #else
  121. #include <unistd.h>
  122. #include <fcntl.h>
  123. #include <termios.h>
  124. // 用于Linux平台的IO函数
  125. // 默认Linux平台均使用utf-8
  126. int fgets_stdin(char **dest, int len) {
  127. *dest = calloc(len, sizeof(char));
  128. if (fgets(*dest, len, stdin) == NULL)
  129. return 0;
  130. return 1;
  131. }
  132. /*
  133. * 函数名: checkStdin
  134. * 目标: 检查stdin缓冲区是否有内容
  135. * 有内容则返回true
  136. * 无内容则返回false
  137. *
  138. * 参考自: https://gist.github.com/SuperH-0630/a4190b89d21c349a8d6882ca71453ae6
  139. */
  140. bool checkStdin(void) {
  141. bool re = false;
  142. int oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
  143. fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
  144. int ch = fgetc(stdin);
  145. CLEAR_FERROR(stdin);
  146. if (ch != EOF) {
  147. ungetc(ch, stdin);
  148. re = true;
  149. }
  150. fcntl(STDIN_FILENO, F_SETFL, oldf);
  151. return re;
  152. }
  153. #endif