#include #include #include #include #include #include // isprint #define BUF_SIZE 4096 // Hexdump 헬퍼 함수 // offset_base: 전체 스트림에서의 현재 위치 (0x0000, 0x0010 ...) void print_hexdump(const unsigned char *buf, ssize_t len, size_t offset_base) { for (size_t i = 0; i < len; i += 16) { // 1. 오프셋 출력 fprintf(stderr, "%08zx: ", offset_base + i); // 2. 16진수 출력 for (size_t j = 0; j < 16; j++) { if (i + j < len) fprintf(stderr, "%02x ", buf[i + j]); else fprintf(stderr, " "); // 패딩 } fprintf(stderr, " |"); // 3. ASCII 출력 for (size_t j = 0; j < 16; j++) { if (i + j < len) { unsigned char c = buf[i + j]; // 출력 가능한 문자면 출력, 아니면 점(.) fprintf(stderr, "%c", isprint(c) ? c : '.'); } } fprintf(stderr, "|\n"); } } int main(int argc, char *argv[]) { int *fds = (int *)malloc(sizeof(int) * argc); int fd_count = 0; // 모드 플래그 int mode_stderr = 0; // --stderr (Raw 출력) int mode_hexdump = 0; // --hexdump (Hex 출력) // 1. 인자 파싱 for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--stderr") == 0) { mode_stderr = 1; continue; } if (strcmp(argv[i], "--hexdump") == 0) { mode_hexdump = 1; continue; } // 일반 파일 열기 int fd = open(argv[i], O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { perror(argv[i]); continue; } fds[fd_count++] = fd; } // 2. 데이터 처리 루프 unsigned char buf[BUF_SIZE]; // unsigned로 변경 ssize_t n; size_t total_bytes = 0; // 전체 스트림 오프셋 추적용 while ((n = read(STDIN_FILENO, buf, BUF_SIZE)) > 0) { // (A) STDOUT 출력 (필수: 파이프 연결 유지) if (write(STDOUT_FILENO, buf, n) != n) { perror("write stdout"); break; } // (B) STDERR 처리 (디버깅용) if (mode_hexdump) { // Hexdump 모드면 예쁘게 포맷팅해서 stderr로 print_hexdump(buf, n, total_bytes); } else if (mode_stderr) { // 그냥 stderr 모드면 Raw 데이터를 stderr로 if (write(STDERR_FILENO, buf, n) != n) { /* ignore */ } } // (C) 파일 출력 for (int i = 0; i < fd_count; i++) { if (write(fds[i], buf, n) != n) { perror("write file"); } } total_bytes += n; } for (int i = 0; i < fd_count; i++) { close(fds[i]); } free(fds); return 0; }