Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

372 lines
8.8 KiB

  1. /* cat - conCATenate STDIN to STDOUT
  2. *
  3. * 24-Apr 1998 hiroyama
  4. *
  5. */
  6. #include "precomp.h"
  7. #pragma hdrstop
  8. #include "fnreg.h"
  9. #if !defined(UNICODE)
  10. #error please compile me for UNICODE
  11. #endif
  12. #ifndef _T
  13. #define _T TEXT
  14. #endif
  15. #define LINENUMBER 0x0001
  16. #define EOL_MARK 0x0002
  17. #define TAB_SPECIAL 0x0004
  18. #define CTRL_SPECIAL 0x0008
  19. #define NO_BUFFERED_OUTPUT 0x0010
  20. #define UNICODE_INPUT 0x0020
  21. #define AUTO_UNICODE_DETECT 0x0040
  22. struct InOutMode {
  23. BOOLEAN fUnicodeInput;
  24. BOOLEAN fMbcsOutput;
  25. BOOLEAN fNeedSwab;
  26. BOOLEAN fLineBuffer;
  27. };
  28. DWORD options = 0;
  29. #define LARGE_BUFSIZE (512)
  30. void usage()
  31. {
  32. static const char str[] =
  33. "cat [-aenotuvV] [filename ...]\n"
  34. "-a same as -u if input begins with BOM\n"
  35. "-e add '$' at the end of line\n"
  36. "-n add line number\n"
  37. "-o suppress buffering for output\n"
  38. "-t show tab character as '^I'\n"
  39. "-u unicode text processing\n"
  40. "-v show control characters as '^' + alphabet, except tab and newline.\n"
  41. "-V show version\n"
  42. "-- declare end of options\n";
  43. fputs(str, stderr);
  44. exit(EXIT_FAILURE);
  45. }
  46. template <class T>
  47. inline void ntoa(T* p, int n, int width)
  48. {
  49. p += width;
  50. *p-- = '\0';
  51. for (--width; width >= 0; --width) {
  52. *p-- = (n % 10) + '0';
  53. n /= 10;
  54. }
  55. }
  56. template <class T>
  57. inline void swap(T& a, T& b)
  58. {
  59. a ^= b ^= a ^= b;
  60. }
  61. /////////////////////////////////////////////////////////
  62. // Complex cat UNICODE helpers
  63. /////////////////////////////////////////////////////////
  64. inline int getcaw(InOutMode mode, FILE* fp)
  65. {
  66. if (mode.fUnicodeInput) {
  67. wchar_t c = fgetwc(fp);
  68. if (c == WEOF)
  69. return EOF;
  70. return c;
  71. }
  72. return fgetc(fp);
  73. }
  74. inline void ungetaw(InOutMode mode, int c, FILE* fp)
  75. {
  76. if (mode.fUnicodeInput)
  77. ungetwc((wchar_t)c, fp);
  78. else
  79. ungetc(c, fp);
  80. }
  81. inline void putcharaw(InOutMode mode, int c)
  82. {
  83. if (mode.fUnicodeInput) {
  84. // if output is dbcs (i.e. tty output), we need to
  85. // translate the unicode character
  86. if (mode.fMbcsOutput) {
  87. // prevent to print BOM
  88. if (c != 0xfeff) {
  89. // translate the output
  90. char buf[2];
  91. int n = wctomb(buf, (wchar_t)c);
  92. putchar(buf[0]);
  93. if (n == 2) {
  94. putchar(buf[1]);
  95. }
  96. }
  97. }
  98. else {
  99. putwchar((wchar_t)c);
  100. }
  101. }
  102. else
  103. putchar(c);
  104. }
  105. inline void swab(int& c)
  106. {
  107. c = ((c & 0xff00) >> 8) | ((unsigned char)c << 8);
  108. }
  109. /////////////////////////////////////////////////////////
  110. // Complex cat
  111. /////////////////////////////////////////////////////////
  112. void complex_cat(const TCHAR* fname)
  113. {
  114. FILE* fp;
  115. if (fname) {
  116. if ((fp = _tfopen(fname, _T("rb"))) == NULL) {
  117. _tperror(fname);
  118. exit(EXIT_FAILURE);
  119. }
  120. if (setvbuf(fp, NULL, _IOFBF, LARGE_BUFSIZE))
  121. perror("setvbuf");
  122. }
  123. else {
  124. // if fname == NULL, take input from stdin.
  125. fp = stdin;
  126. }
  127. static bool tol = true; // Top Of Line
  128. static long lineno = 0;
  129. int c, c2;
  130. // Initialize In/Out mode
  131. InOutMode inOutMode = {
  132. !!(options & UNICODE_INPUT),
  133. false,
  134. false,
  135. // if buffered mode and stdout is tty, flush buffer at each EOL
  136. !(options & NO_BUFFERED_OUTPUT) && _isatty(_fileno(stdout)),
  137. };
  138. // UNICODE initialization
  139. if (inOutMode.fUnicodeInput) {
  140. // sample the first word for BOM detection
  141. c = fgetwc(fp);
  142. init_unicode:
  143. _setmode(_fileno(fp), _O_BINARY);
  144. if (_isatty(_fileno(stdout))) {
  145. // if the output is tty,
  146. // need to convert UNICODE to MBCS on output
  147. inOutMode.fMbcsOutput = true;
  148. }
  149. // try to process the BOM
  150. if (c == 0xfeff) {
  151. putcharaw(inOutMode, c);
  152. }
  153. else if (c == 0xfffe) {
  154. inOutMode.fNeedSwab = true;
  155. swab(c);
  156. putcharaw(inOutMode, c);
  157. }
  158. else {
  159. ungetwc((wchar_t)c, fp);
  160. }
  161. }
  162. else if (options & AUTO_UNICODE_DETECT) {
  163. // sample and examine the first word to see if it's UNICODE BOM
  164. c = fgetwc(fp);
  165. if (c == 0xfffe || c == 0xfeff) {
  166. inOutMode.fUnicodeInput = true;
  167. goto init_unicode;
  168. }
  169. ungetwc((wchar_t)c, fp);
  170. }
  171. #ifdef MEASURE_PERF
  172. DWORD start = ::GetTickCount();
  173. #endif
  174. while ((c = getcaw(inOutMode, fp)) != EOF) {
  175. if (tol) {
  176. // process line number
  177. tol = false;
  178. if (options & LINENUMBER) {
  179. if (inOutMode.fUnicodeInput && !inOutMode.fMbcsOutput) {
  180. wchar_t buf[5];
  181. ntoa(buf, ++lineno, 4);
  182. fputws(buf, stdout);
  183. fputws(L": ", stdout);
  184. }
  185. else {
  186. char buf[5];
  187. ntoa(buf, ++lineno, 4);
  188. fputs(buf, stdout);
  189. fputs(": ", stdout);
  190. }
  191. }
  192. }
  193. if (inOutMode.fNeedSwab)
  194. swab(c);
  195. switch (c) {
  196. case '\r':
  197. c2 = getcaw(inOutMode, fp);
  198. if (c2 != '\n') {
  199. ungetaw(inOutMode, c2, fp);
  200. goto normal_input;
  201. }
  202. // fall through
  203. case '\n':
  204. if (options & EOL_MARK) {
  205. putcharaw(inOutMode, '$');
  206. }
  207. if (c != '\n') {
  208. putcharaw(inOutMode, c);
  209. c = c2;
  210. }
  211. putcharaw(inOutMode, c);
  212. if (inOutMode.fLineBuffer) {
  213. // if line buffer mode, flush it
  214. fflush(stdout);
  215. }
  216. tol = true;
  217. break;
  218. case '\t':
  219. if (options & TAB_SPECIAL) {
  220. fputs("^I", stdout);
  221. }
  222. else {
  223. putcharaw(inOutMode, c);
  224. }
  225. break;
  226. default:
  227. normal_input:
  228. if (c < 0x20 && (options & CTRL_SPECIAL)) {
  229. putcharaw(inOutMode, '^');
  230. c += '@';
  231. }
  232. putcharaw(inOutMode, c);
  233. break;
  234. }
  235. }
  236. if (fname) {
  237. fclose(fp);
  238. }
  239. #ifdef MEASURE_PERF
  240. DWORD end = ::GetTickCount();
  241. fprintf(stderr, "delta=%u\n", end - start);
  242. #endif
  243. }
  244. void cat(const TCHAR* fname = NULL)
  245. {
  246. static bool is1st = true;
  247. if (is1st) {
  248. is1st = false;
  249. if (options & NO_BUFFERED_OUTPUT) {
  250. // non buffered mode
  251. if (setvbuf(stdout, NULL, _IONBF, 0))
  252. perror("setvbuf");
  253. }
  254. else {
  255. if (setvbuf(stdout, NULL, _IOFBF, LARGE_BUFSIZE))
  256. perror("setvbuf");
  257. }
  258. }
  259. complex_cat(fname);
  260. }
  261. void parse_option(const TCHAR* s, bool& eoo) // eoo: end of options
  262. {
  263. extern char version[];
  264. while (*++s) {
  265. switch (*s) {
  266. case _T('-'):
  267. eoo = true;
  268. return;
  269. case _T('a'):
  270. options |= AUTO_UNICODE_DETECT;
  271. break;
  272. case _T('e'):
  273. options |= EOL_MARK;
  274. break;
  275. case _T('n'):
  276. options |= LINENUMBER;
  277. break;
  278. case _T('v'):
  279. options |= CTRL_SPECIAL;
  280. break;
  281. case _T('t'):
  282. options |= TAB_SPECIAL;
  283. break;
  284. case _T('o'):
  285. options |= NO_BUFFERED_OUTPUT;
  286. break;
  287. case _T('u'):
  288. options |= UNICODE_INPUT;
  289. break;
  290. case _T('V'):
  291. fputs(version, stderr);
  292. exit(EXIT_SUCCESS);
  293. default:
  294. usage(); // never returns
  295. }
  296. }
  297. }
  298. #ifdef UNICODE
  299. #define main wmain
  300. #endif
  301. extern "C"
  302. int __cdecl main(int argc, TCHAR** argv)
  303. {
  304. int n = 0;
  305. bool eoo = false;
  306. fnexpand(&argc, &argv);
  307. setlocale(LC_ALL, "");
  308. // set stdout binary mode
  309. _setmode(_fileno(stdout), _O_BINARY);
  310. while (--argc) {
  311. if (**++argv == _T('-') && !eoo) {
  312. parse_option(*argv, eoo);
  313. }
  314. else {
  315. ++n;
  316. eoo = true;
  317. cat(*argv);
  318. }
  319. }
  320. if (n == 0) {
  321. _setmode(_fileno(stdin), _O_BINARY);
  322. cat();
  323. }
  324. return EXIT_SUCCESS;
  325. }