// Base64 table const char* base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// ------------ 변환 함수들 ------------
// Base64 encode bool encodeBase64(const char* input, char* output, size_t outSize) { // 입력 검증 if (!input || !output || outSize == 0) { return false; }
size_t len = strlen(input); size_t required = ((len + 2) / 3) * 4 + 1; // +1 for null terminator
// 버퍼 크기 검증 if (outSize < required) { return false; }
size_t outPos = 0;
for (size_t i = 0; i < len; i += 3) { unsigned char b1 = (unsigned char)input[i]; unsigned char b2 = (i + 1 < len) ? (unsigned char)input[i + 1] : 0; unsigned char b3 = (i + 2 < len) ? (unsigned char)input[i + 2] : 0;
output[outPos++] = base64_table[(b1 >> 2) & 0x3F]; output[outPos++] = base64_table[(((b1 & 0x03) << 4) | ((b2 >> 4) & 0x0F)) & 0x3F]; output[outPos++] = (i + 1 < len) ? base64_table[(((b2 & 0x0F) << 2) | ((b3 >> 6) & 0x03)) & 0x3F] : '='; output[outPos++] = (i + 2 < len) ? base64_table[b3 & 0x3F] : '='; }
output[outPos] = '\0'; return true; }
// Base64 decode bool decodeBase64(const char* input, char* output, size_t outSize) { // Base64 디코딩 테이블 (signed char 사용) static const int8_t table[256] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 };
// 입력 검증 if (!input || !output || outSize == 0) { return false; }
size_t len = strlen(input); size_t outPos = 0; unsigned int val = 0; int bits = -8;
for (size_t i = 0; i < len; i++) { unsigned char c = (unsigned char)input[i];
// 공백 문자 스킵 if (c == ' ' || c == '\n' || c == '\r' || c == '\t') { continue; }
// 패딩 문자 처리 if (c == '=') { break; }
// 디코딩 테이블 조회 int8_t decoded = table[c]; if (decoded == -1) { // 잘못된 Base64 문자 return false; }
val = (val << 6) | (unsigned int)decoded; bits += 6;
if (bits >= 0) { // 버퍼 오버플로우 체크 if (outPos >= outSize - 1) { return false; } output[outPos++] = (char)((val >> bits) & 0xFF); bits -= 8; } }
output[outPos] = '\0'; return true; }
// Text to Hex bool textToHex(const char* input, char* output, size_t outSize) { static const char hexDigits[] = "0123456789ABCDEF";
if (!input || !output || outSize == 0) { return false; }
size_t inputLen = strlen(input);
if (inputLen == 0) { output[0] = '\0'; return true; }
// 필요한 크기: inputLen * 3 (마지막 공백 포함) + 1 (null) // 실제로는 inputLen * 3 - 1 + 1 = inputLen * 3 if (outSize < inputLen * 3) { return false; }
size_t outPos = 0;
for (size_t i = 0; i < inputLen; i++) { unsigned char byte = (unsigned char)input[i];
output[outPos++] = hexDigits[(byte >> 4) & 0x0F]; // 상위 4비트 output[outPos++] = hexDigits[byte & 0x0F]; // 하위 4비트
// 마지막 문자가 아니면 공백 추가 if (i < inputLen - 1) { output[outPos++] = ' '; } }
output[outPos] = '\0'; return true; }
// Hex to Text bool hexToText(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t outPos = 0; size_t i = 0;
while (input[i] != '\0' && outPos < outSize - 1) { // 공백 및 구분자 스킵 while (input[i] == ' ' || input[i] == ':' || input[i] == '-' || input[i] == '\t' || input[i] == '\n' || input[i] == '\r') { if (input[i] == '\0') break; i++; }
if (input[i] == '\0') break;
// 첫 번째 hex 문자 확인 int high = hexCharToValue(input[i]); if (high == -1) { return false; }
// 두 번째 hex 문자 확인 (범위 체크) if (input[i + 1] == '\0') { return false; // 홀수 개의 hex 문자 }
int low = hexCharToValue(input[i + 1]); if (low == -1) { return false; }
// 바이트 조합 output[outPos++] = (char)((high << 4) | low); i += 2; }
output[outPos] = '\0'; return true; }
// Text to Decimal bool textToDec(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t inputLen = strlen(input);
if (inputLen == 0) { output[0] = '\0'; return true; }
// 필요한 크기: 최대 4바이트 * inputLen if (outSize < inputLen * 4 + 1) { return false; }
size_t outPos = 0;
for (size_t i = 0; i < inputLen; i++) { unsigned char byte = (unsigned char)input[i];
// 버퍼 체크 (최대 4바이트 필요: "255 ") if (outPos + 4 >= outSize) { return false; }
// 숫자 변환 size_t numLen = uintToStr(byte, output + outPos); outPos += numLen;
// 마지막이 아니면 공백 추가 if (i < inputLen - 1) { output[outPos++] = ' '; } }
output[outPos] = '\0'; return true; }
// Decimal to Text bool decToText(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t outPos = 0; size_t i = 0;
while (input[i] != '\0') { // 숫자가 아닌 문자 스킵 while (input[i] != '\0' && !isdigit((unsigned char)input[i])) { i++; }
if (input[i] == '\0') break;
// 숫자 파싱 (수동으로 안전하게) int num = 0; int digitCount = 0;
while (isdigit((unsigned char)input[i])) { int digit = input[i] - '0';
// 오버플로우 체크 (255 초과 방지) if (num > 25 || (num == 25 && digit > 5)) { return false; // 255 초과 }
num = num * 10 + digit; digitCount++; i++;
// 숫자가 너무 길면 에러 (최대 3자리) if (digitCount > 3) { return false; } }
// 출력 버퍼 체크 if (outPos >= outSize - 1) { return false; }
output[outPos++] = (char)num; }
output[outPos] = '\0'; return true; }
// Decimal to Hex bool decToHex(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t outPos = 0; size_t i = 0; bool firstNumber = true;
while (input[i] != '\0') { // 숫자가 아닌 문자 스킵 while (input[i] != '\0' && !isdigit((unsigned char)input[i])) { i++; }
if (input[i] == '\0') break;
// 10진수 파싱 unsigned long num = 0; int digitCount = 0;
while (isdigit((unsigned char)input[i])) { int digit = input[i] - '0';
// 오버플로우 체크 (UINT_MAX = 4294967295) if (num > (UINT_MAX - digit) / 10) { return false; }
num = num * 10 + digit; digitCount++; i++;
// 최대 10자리 if (digitCount > 10) { return false; } }
// 공백 추가 (첫 번째가 아닌 경우) if (!firstNumber) { if (outPos >= outSize - 1) { return false; } output[outPos++] = ' '; }
// 16진수 변환 (최대 8자리 필요) if (outPos + 8 >= outSize) { return false; }
size_t hexLen = uintToHex((unsigned int)num, output + outPos); outPos += hexLen; firstNumber = false; }
output[outPos] = '\0'; return true; }
// Hex to Decimal bool hexToDec(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t outPos = 0; size_t i = 0; bool firstNumber = true;
while (input[i] != '\0') { // hex 숫자가 아닌 문자 스킵 while (input[i] != '\0' && !isxdigit((unsigned char)input[i])) { i++; }
if (input[i] == '\0') break;
// hex 파싱 unsigned long num = 0; int digitCount = 0;
while (isxdigit((unsigned char)input[i])) { int val = hexCharToValue(input[i]); if (val == -1) { return false; }
// 오버플로우 체크 (UINT_MAX = 0xFFFFFFFF) if (num > (UINT_MAX >> 4)) { return false; }
num = (num << 4) | val; digitCount++; i++;
// 최대 8자리 (32비트) if (digitCount > 8) { return false; } }
// 공백 추가 (첫 번째가 아닌 경우) if (!firstNumber) { if (outPos >= outSize - 1) { return false; } output[outPos++] = ' '; }
// 10진수 변환 (최대 10자리 필요) if (outPos + 10 >= outSize) { return false; }
size_t decLen = uintToDecimal((unsigned int)num, output + outPos); outPos += decLen; firstNumber = false; }
output[outPos] = '\0'; return true; }
// Decimal to Octal bool decToOctal(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t outPos = 0; size_t i = 0; bool firstNumber = true;
// 32비트 unsigned int는 8진수로 최대 11자리 필요 (예: 4294967295 -> 37777777777) const size_t MAX_OCTAL_DIGITS = 11;
while (input[i] != '\0') { // 숫자가 아닌 문자 스킵 while (input[i] != '\0' && !isdigit((unsigned char)input[i])) { i++; }
if (input[i] == '\0') break;
// 10진수 파싱 unsigned long num = 0; int digitCount = 0;
while (isdigit((unsigned char)input[i])) { int digit = input[i] - '0';
// 오버플로우 체크 (unsigned int 범위: UINT_MAX) // unsigned long을 사용하여 파싱하더라도, 최종 변환은 unsigned int 범위 내에서만 처리합니다. // num > (UINT_MAX - digit) / 10 대신, num이 이미 UINT_MAX를 초과했는지 확인하는 것이 더 명확합니다. // 하지만 기존 코드의 로직을 유지하면서 UINT_MAX까지만 지원합니다. if (num > (UINT_MAX - digit) / 10) { // 숫자가 UINT_MAX (4294967295)를 초과합니다. return false; }
num = num * 10 + digit; digitCount++; i++;
// 최대 10자리 (UINT_MAX는 10자리) if (digitCount > 10) { // 10자리를 초과하면 UINT_MAX를 초과한 것으로 간주하여 실패 처리 return false; } }
// 10자리인데 값이 UINT_MAX를 초과하는 경우 (예: 4294967296)를 처리하기 위해 // 위 오버플로우 체크가 중요합니다.
// 공백 추가 (첫 번째가 아닌 경우) if (!firstNumber) { if (outPos >= outSize - 1) { return false; // 버퍼 부족 } output[outPos++] = ' '; }
// 8진수 변환 (최대 11자리 필요) if (outPos + MAX_OCTAL_DIGITS >= outSize) { return false; // 버퍼 부족 }
// 8진수 변환 및 길이 업데이트 size_t octalLen = uintToOctal((unsigned int)num, output + outPos); if (octalLen == 0) { return false; // 변환 실패 }
outPos += octalLen; firstNumber = false; }
output[outPos] = '\0'; return true; }
// Octal to Decimal bool octalToDec(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t outPos = 0; size_t i = 0; bool firstNumber = true;
// 32비트 unsigned int는 10진수로 최대 10자리 필요 const size_t MAX_DECIMAL_DIGITS = 10;
while (input[i] != '\0') { // 숫자가 아닌 문자 스킵 while (input[i] != '\0' && !isdigit((unsigned char)input[i])) { i++; }
if (input[i] == '\0') break;
// 8진수 파싱 및 10진수로 변환 unsigned long num = 0;
// 8진수 숫자의 시작 위치 기억 size_t octalStart = i;
while (isdigit((unsigned char)input[i])) { int digit = input[i] - '0';
// 8진수 유효성 검사 (숫자가 0~7 범위인지 확인) if (digit >= 8) { // '8' 또는 '9'는 8진수 숫자가 아님 return false; }
// 오버플로우 체크 (num * 8 + digit <= UINT_MAX) if (num > UINT_MAX / 8 || (num == UINT_MAX / 8 && digit > UINT_MAX % 8)) { // 결과가 unsigned int 범위를 초과합니다. return false; }
num = num * 8 + digit; i++; }
// 실제로 파싱된 숫자가 없는 경우 (예: " ")는 위에서 이미 처리됨 // 하지만 만약 숫자를 처리하지 못하고 루프를 빠져나왔다면 문제가 발생할 수 있지만, // 위 로직은 isdigit을 통과한 후 while(isdigit)에서 파싱하므로 octalStart와 i가 같다면 // while(isdigit)의 첫 번째 문자가 8 또는 9였다는 뜻이며, 이는 이미 false를 반환합니다.
// 공백 추가 (첫 번째 숫자가 아닌 경우) if (!firstNumber) { if (outPos >= outSize - 1) { return false; // 버퍼 부족 } output[outPos++] = ' '; }
// 10진수 변환 (최대 10자리 필요) if (outPos + MAX_DECIMAL_DIGITS >= outSize) { return false; // 버퍼 부족 }
// 10진수 변환 및 길이 업데이트 size_t decLen = uintToDec((unsigned int)num, output + outPos); if (decLen == 0) { return false; // 변환 실패 }
outPos += decLen; firstNumber = false; }
output[outPos] = '\0'; return true; }
// Text to UTF-7 bool textToUtf7(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t inputLen = strlen(input); size_t outPos = 0; size_t i = 0; const char* direct_chars = " '(),./";
while (i < inputLen) { unsigned char current_char = (unsigned char)input[i];
// 1. Direct Character if (isalnum(current_char) || strchr(direct_chars, current_char)) { if (outPos >= outSize - 1) return false; output[outPos++] = (char)current_char; i++; } // 2. '+' 특수 처리 else if (current_char == '+') { if (outPos + 2 >= outSize) return false; output[outPos++] = '+'; output[outPos++] = '-'; i++; } // 3. Encoded Characters else { // UTF-16 Big Endian으로 변환 unsigned char block_bytes[6]; // 최대 3문자 = 6바이트 size_t block_byte_idx = 0;
while (i < inputLen && !isalnum((unsigned char)input[i]) && !strchr(direct_chars, (unsigned char)input[i]) && input[i] != '+' && block_byte_idx < 6) {
unsigned char c = (unsigned char)input[i];
// UTF-16 Big Endian 변환 block_bytes[block_byte_idx++] = 0x00; // 상위 바이트 (ASCII는 0) block_bytes[block_byte_idx++] = c; // 하위 바이트
i++; }
// '+' 마커 if (outPos >= outSize - 1) return false; output[outPos++] = '+';
// Base64 인코딩 size_t b64_len = encode_base64_block(block_bytes, block_byte_idx, output + outPos, outSize - outPos); if (b64_len == 0) return false; outPos += b64_len;
// '-' 마커 if (outPos >= outSize - 1) return false; output[outPos++] = '-'; } }
output[outPos] = '\0'; return true; }
// UTF-7 to Text bool utf7ToText(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t inputLen = strlen(input); size_t outPos = 0; size_t i = 0;
// 디코딩된 UTF-16 바이트를 저장할 임시 버퍼 (충분히 크게 설정) unsigned char decoded_bytes[512];
// Base64 블록 저장을 위한 임시 버퍼를 정적 크기로 선언 #define MAX_B64_BLOCK_SIZE 512 char temp_b64_block[MAX_B64_BLOCK_SIZE];
while (i < inputLen) { // ... (생략) ...
// 1. Base64 인코딩 블록 시작 감지 if (input[i] == '+') { i++; // '+' 건너뛰기
// 1-1. "+-" 패턴 처리 (인코딩된 '+' 문자) if (input[i] == '-') { // ... (생략) ... output[outPos++] = '+'; i++; continue; }
// 1-2. Base64 블록 파싱 (다음 '-'가 나올 때까지) size_t block_start = i; size_t block_len = 0;
// Base64 블록 추출 while (i < inputLen && input[i] != '-') { if (block_len >= MAX_B64_BLOCK_SIZE - 1) { return false; // 임시 버퍼 오버플로우 방지 } i++; block_len++; }
// 임시 버퍼에 Base64 문자열 복사 // VLA 대신 정적 버퍼를 사용하고 block_len만큼 복사 memcpy(temp_b64_block, input + block_start, block_len); temp_b64_block[block_len] = '\0'; // 널 문자 추가
// Base64 디코딩 (결과는 UTF-16 바이트 스트림) size_t decoded_len = decode_base64_block(temp_b64_block, block_len, decoded_bytes, sizeof(decoded_bytes));
if (decoded_len == 0 && block_len > 0) { return false; // 디코딩 실패 }
// UTF-16 바이트에서 ASCII 문자 추출 (0x00 XX 형태에서 XX만 추출) for (size_t j = 0; j < decoded_len; j++) { // 홀수 인덱스 (하위 바이트)만 유효한 ASCII 문자 if (j % 2 == 1) { if (outPos >= outSize - 1) return false; output[outPos++] = (char)decoded_bytes[j]; } }
// 디코딩 후 '-'가 있다면 건너뛰기 if (i < inputLen && input[i] == '-') { i++; } } // 2. 직접 표현 문자 처리 else { if (outPos >= outSize - 1) return false; output[outPos++] = input[i]; i++; } }
output[outPos] = '\0'; return true; }
// Hex to UCS-2 bool hexToUcs2(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t outPos = 0; size_t i = 0;
static const char hexDigits[] = "0123456789ABCDEF";
while (input[i] != '\0') { // 공백 및 구분자 스킵 while (input[i] == ' ' || input[i] == ':' || input[i] == '-' || input[i] == '\t' || input[i] == '\n' || input[i] == '\r') { if (input[i] == '\0') break; i++; } if (input[i] == '\0') break;
// 첫 번째 hex 문자 (상위 nibble) int high = hexCharToValue(input[i]); if (high == -1) { return false; }
// 두 번째 hex 문자 (하위 nibble) if (input[i + 1] == '\0') { return false; // 홀수 개의 hex 문자 } int low = hexCharToValue(input[i + 1]); if (low == -1) { return false; }
unsigned char byte = (unsigned char)((high << 4) | low);
// UCS-2 Little Endian: 낮은 바이트 먼저, 그 다음 00 // 출력 버퍼에 4자리 Hex로 기록: byte(Low) + 00(High)
if (outPos + 4 >= outSize) { return false; // 버퍼 부족 }
// 낮은 바이트 (Little Endian) output[outPos++] = hexDigits[byte >> 4]; output[outPos++] = hexDigits[byte & 0x0F];
// 높은 바이트 (항상 00 for BMP) output[outPos++] = '0'; output[outPos++] = '0';
i += 2; }
output[outPos] = '\0'; return true; }
// UCS-2 to Hex bool ucs2ToHex(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t outPos = 0; size_t i = 0;
static const char hexDigits[] = "0123456789ABCDEF";
while (input[i] != '\0') { // 공백 및 구분자 스킵 while (input[i] == ' ' || input[i] == ':' || input[i] == '-' || input[i] == '\t' || input[i] == '\n' || input[i] == '\r') { if (input[i] == '\0') break; i++; } if (input[i] == '\0') break;
// UCS-2는 4자리 Hex 단위여야 함 (2바이트: Low + High) // 4자리 읽기
int nibbles[4]; for (int j = 0; j < 4; j++) { if (input[i] == '\0') { return false; // 4자리 미만으로 끝남 }
// 구분자 다시 스킵 (만약 중간에 있을 수 있음) while (input[i] == ' ' || input[i] == ':' || input[i] == '-' || input[i] == '\t' || input[i] == '\n' || input[i] == '\r') { i++; if (input[i] == '\0') return false; }
nibbles[j] = hexCharToValue(input[i]); if (nibbles[j] == -1) { return false; // 유효하지 않은 hex 문자 } i++; }
// Little Endian: 낮은 바이트가 먼저 옴 // nibbles[0..1]: Low byte, nibbles[2..3]: High byte unsigned char lowByte = (unsigned char)((nibbles[0] << 4) | nibbles[1]); unsigned char highByte = (unsigned char)((nibbles[2] << 4) | nibbles[3]);
// BMP 범위에서는 highByte가 00이어야 정상 (U+0000 ~ U+FFFF) // 프로그램의 기존 용도상 highByte가 00이 아닌 경우도 허용할지 결정 // 여기서는 경고 없이 lowByte만 사용 (대부분의 용례가 ASCII이기 때문) // 필요시 아래 주석 해제하여 엄격히 검사 가능 // if (highByte != 0) return false; // 서러게이트나 비BMP 문자 거부
// 출력: lowByte만 Hex로 변환 (2자리) if (outPos + 2 >= outSize) { return false; // 버퍼 부족 }
output[outPos++] = hexDigits[lowByte >> 4]; output[outPos++] = hexDigits[lowByte & 0x0F]; }
output[outPos] = '\0'; return true; }
// Text to Binary bool textToBinary(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t inputLen = strlen(input);
if (inputLen == 0) { output[0] = '\0'; return true; }
size_t required = inputLen * 9; if (outSize < required) { return false; }
size_t outPos = 0;
for (size_t i = 0; i < inputLen; i++) { unsigned char byte = (unsigned char)input[i];
// 비트마스크로 직접 변환 output[outPos++] = (byte & 0x80) ? '1' : '0'; output[outPos++] = (byte & 0x40) ? '1' : '0'; output[outPos++] = (byte & 0x20) ? '1' : '0'; output[outPos++] = (byte & 0x10) ? '1' : '0'; output[outPos++] = (byte & 0x08) ? '1' : '0'; output[outPos++] = (byte & 0x04) ? '1' : '0'; output[outPos++] = (byte & 0x02) ? '1' : '0'; output[outPos++] = (byte & 0x01) ? '1' : '0';
// 마지막이 아니면 공백 if (i < inputLen - 1) { output[outPos++] = ' '; } }
output[outPos] = '\0'; return true; }
// Binary to Text bool binaryToText(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
char temp[9]; int tempIdx = 0; size_t outPos = 0;
for (size_t i = 0; input[i] != '\0'; i++) { if (input[i] == '0' || input[i] == '1') { if (tempIdx >= 8) { // 8비트 초과 return false; } temp[tempIdx++] = input[i];
if (tempIdx == 8) { temp[8] = '\0';
unsigned char byte; if (!parseBinaryByte(temp, &byte)) { return false; }
if (outPos >= outSize - 1) { return false; }
output[outPos++] = (char)byte; tempIdx = 0; } } else if (input[i] == ' ' || input[i] == '\t' || input[i] == '\n' || input[i] == '\r') { // 공백 허용 continue; } else { // 잘못된 문자 return false; } }
// 남은 비트 체크 if (tempIdx != 0) { // 불완전한 바이트 return false; }
output[outPos] = '\0'; return true; }
// Escape bool escapeString(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t inputLen = strlen(input); size_t required = inputLen * 6 + 1; // 각 문자당 \uXXXX (6바이트)
if (outSize < required) { return false; }
size_t outPos = 0;
for (size_t i = 0; i < inputLen; i++) { unsigned char c = (unsigned char)input[i]; snprintf(output + outPos, outSize - outPos, "\\u%04x", c); outPos += 6; }
output[outPos] = '\0'; return true; }
// Unescape bool unescapeString(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t outPos = 0;
for (size_t i = 0; input[i] != '\0'; i++) { if (input[i] == '\\') { if (input[i + 1] == '\0') { return false; }
i++;
switch (input[i]) { case 'n': if (outPos >= outSize - 1) return false; output[outPos++] = '\n'; break; case 'r': if (outPos >= outSize - 1) return false; output[outPos++] = '\r'; break; case 't': if (outPos >= outSize - 1) return false; output[outPos++] = '\t'; break; case '\\': if (outPos >= outSize - 1) return false; output[outPos++] = '\\'; break; case '\"': if (outPos >= outSize - 1) return false; output[outPos++] = '\"'; break; case '\'': if (outPos >= outSize - 1) return false; output[outPos++] = '\''; break; case 'b': if (outPos >= outSize - 1) return false; output[outPos++] = '\b'; break; case 'f': if (outPos >= outSize - 1) return false; output[outPos++] = '\f'; break; case '0': if (outPos >= outSize - 1) return false; output[outPos++] = '\0'; break;
case 'u': { // \uXXXX if (input[i + 1] == '\0' || input[i + 2] == '\0' || input[i + 3] == '\0' || input[i + 4] == '\0') { return false; }
int d1 = hexCharToValue(input[i + 1]); int d2 = hexCharToValue(input[i + 2]); int d3 = hexCharToValue(input[i + 3]); int d4 = hexCharToValue(input[i + 4]);
if (d1 < 0 || d2 < 0 || d3 < 0 || d4 < 0) { return false; }
unsigned int codepoint = (d1 << 12) | (d2 << 8) | (d3 << 4) | d4;
// UTF-8로 인코딩 (최대 4바이트) char utf8[4]; size_t utf8Len = encodeUTF8(codepoint, utf8);
if (utf8Len == 0 || outPos + utf8Len > outSize - 1) { return false; }
for (size_t j = 0; j < utf8Len; j++) { output[outPos++] = utf8[j]; }
i += 4; break; }
case 'x': { // \xXX if (input[i + 1] == '\0' || input[i + 2] == '\0') { return false; }
int h1 = hexCharToValue(input[i + 1]); int h2 = hexCharToValue(input[i + 2]);
if (h1 < 0 || h2 < 0) { return false; }
if (outPos >= outSize - 1) return false; output[outPos++] = (char)((h1 << 4) | h2); i += 2; break; }
default: return false; } } else { if (outPos >= outSize - 1) return false; output[outPos++] = input[i]; } }
output[outPos] = '\0'; return true; }
// HTML Encode bool encodeHTML(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t outPos = 0;
for (size_t i = 0; input[i] != '\0'; i++) { const char* entity = NULL; size_t entityLen = 0; unsigned char c = (unsigned char)input[i];
// HTML 엔티티 매핑 switch (c) { case '&': entity = "&"; entityLen = 5; break; case '<': entity = "<"; entityLen = 4; break; case '>': entity = ">"; entityLen = 4; break; case '"': entity = """; entityLen = 6; break; case '\'': entity = "'"; entityLen = 5; break; // 추가 엔티티 case '\n': entity = " "; entityLen = 5; break; case '\r': entity = " "; entityLen = 5; break; default: // 출력 불가능한 문자는 숫자 엔티티로 if (c < 32 || c == 127) { // &#XX; 형식 (최대 6바이트) if (outPos + 6 >= outSize) return false; int written = snprintf(output + outPos, outSize - outPos, "&#%u;", c); if (written < 0 || (size_t)written >= outSize - outPos) { return false; } outPos += written; continue; } // 일반 문자 if (outPos >= outSize - 1) return false; output[outPos++] = c; continue; }
// 엔티티 쓰기 if (outPos + entityLen >= outSize) { return false; }
memcpy(output + outPos, entity, entityLen); outPos += entityLen; }
output[outPos] = '\0'; return true; }
// HTML Decode bool decodeHTML(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
size_t outPos = 0; size_t inputLen = strlen(input);
for (size_t i = 0; i < inputLen; i++) { if (outPos >= outSize - 1) { return false; }
if (input[i] == '&') { size_t remaining = inputLen - i; bool matched = false;
// 각 엔티티의 최소 길이 체크 후 비교 if (remaining >= 5 && memcmp(input + i, "&", 5) == 0) { output[outPos++] = '&'; i += 4; matched = true; } else if (remaining >= 4 && memcmp(input + i, "<", 4) == 0) { output[outPos++] = '<'; i += 3; matched = true; } else if (remaining >= 4 && memcmp(input + i, ">", 4) == 0) { output[outPos++] = '>'; i += 3; matched = true; } else if (remaining >= 6 && memcmp(input + i, """, 6) == 0) { output[outPos++] = '"'; i += 5; matched = true; } else if (remaining >= 5 && memcmp(input + i, "'", 5) == 0) { output[outPos++] = '\''; i += 4; matched = true; }
if (!matched) { output[outPos++] = input[i]; } } else { output[outPos++] = input[i]; } }
output[outPos] = '\0'; return true; }
// HexToBase64 bool hexToBase64(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
unsigned char binary[MAX_BUFFER]; size_t binPos = 0; size_t i = 0;
// 1. Hex → binary while (input[i] != '\0' && binPos < MAX_BUFFER) { // 구분자 스킵 while (input[i] == ' ' || input[i] == ':' || input[i] == '-' || input[i] == '\t' || input[i] == '\n' || input[i] == '\r') { i++; } if (input[i] == '\0') break;
int high = hexCharToValue(input[i++]); if (high == -1) return false;
if (input[i] == '\0') return false; // 홀수 hex int low = hexCharToValue(input[i++]); if (low == -1) return false;
binary[binPos++] = (unsigned char)((high << 4) | low); }
if (binPos == 0) { output[0] = '\0'; return true; }
static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t outPos = 0; size_t pos = 0;
// 2. binary → Base64 (3바이트씩 처리) while (pos < binPos) { size_t remaining = binPos - pos; // 남은 바이트 수 (1~3)
unsigned char b1 = binary[pos++]; unsigned char b2 = (remaining >= 2) ? binary[pos++] : 0; unsigned char b3 = (remaining >= 3) ? binary[pos++] : 0;
unsigned int block = (b1 << 16) | (b2 << 8) | b3;
if (outPos + 4 >= outSize) return false;
output[outPos++] = base64_chars[(block >> 18) & 0x3F]; output[outPos++] = base64_chars[(block >> 12) & 0x3F]; output[outPos++] = (remaining >= 2) ? base64_chars[(block >> 6) & 0x3F] : '='; output[outPos++] = (remaining >= 3) ? base64_chars[block & 0x3F] : '='; }
output[outPos] = '\0'; return true; }
// Base64ToHex bool base64ToHex(const char* input, char* output, size_t outSize) { if (!input || !output || outSize == 0) { return false; }
// Base64 디코딩을 위한 임시 바이너리 버퍼 unsigned char binary[MAX_BUFFER]; size_t binPos = 0;
size_t i = 0; while (input[i] != '\0') { // 4개 문자씩 하나의 블록 처리 char block[4]; int val[4]; int valCount = 0;
// Base64 문자 4개 읽기 (패딩 = 허용) for (int j = 0; j < 4; j++) { if (input[i] == '\0') { return false; // 블록 중간에 끝나면 오류 }
char c = input[i++]; if (c == '=') { block[j] = '='; val[j] = -1; // 패딩 표시 } else { int v = base64_char_to_value(c); if (v == -1) { return false; // 유효하지 않은 Base64 문자 } block[j] = c; val[j] = v; valCount++; } }
// 패딩 처리: 마지막 블록에서 = 개수 확인 if (block[3] == '=') { if (block[2] != '=') return false; // 잘못된 패딩 if (valCount != 2) return false; } else if (block[2] == '=') { if (valCount != 3) return false; } else { if (valCount != 4) return false; }
// 24비트 블록 구성 unsigned int bits = 0; if (valCount >= 1) bits |= (val[0] << 18); if (valCount >= 2) bits |= (val[1] << 12); if (valCount >= 3) bits |= (val[2] << 6); if (valCount >= 4) bits |= (val[3]);
// 출력 바이트 수 계산 int byteCount = (valCount * 6) / 8; // 2 -> 1바이트, 3 -> 2바이트, 4 -> 3바이트
// 바이너리 버퍼에 저장 if (byteCount >= 1) { if (binPos >= MAX_BUFFER) return false; binary[binPos++] = (unsigned char)((bits >> 16) & 0xFF); } if (byteCount >= 2) { if (binPos >= MAX_BUFFER) return false; binary[binPos++] = (unsigned char)((bits >> 8) & 0xFF); } if (byteCount >= 3) { if (binPos >= MAX_BUFFER) return false; binary[binPos++] = (unsigned char)(bits & 0xFF); } }
// 바이너리를 Hex 문자열로 변환 size_t outPos = 0; static const char hexDigits[] = "0123456789ABCDEF";
for (size_t k = 0; k < binPos; k++) { if (outPos + 2 >= outSize) { return false; // 출력 버퍼 부족 } unsigned char byte = binary[k]; output[outPos++] = hexDigits[byte >> 4]; output[outPos++] = hexDigits[byte & 0x0F]; }
output[outPos] = '\0'; return true; }
// Clipboard helper bool copyToClipboard(const char* text) { if (!text) { return false; }
size_t len = strlen(text); if (len == 0) { return false; }
// ANSI 메모리 준비 HGLOBAL hMemAnsi = GlobalAlloc(GMEM_MOVEABLE, len + 1); if (!hMemAnsi) { return false; }
char* pMemAnsi = (char*)GlobalLock(hMemAnsi); if (!pMemAnsi) { GlobalFree(hMemAnsi); return false; }
memcpy(pMemAnsi, text, len + 1); GlobalUnlock(hMemAnsi);
// 유니코드 메모리 준비 int wideLen = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); HGLOBAL hMemUnicode = NULL;
if (wideLen > 0) { hMemUnicode = GlobalAlloc(GMEM_MOVEABLE, wideLen * sizeof(wchar_t)); if (hMemUnicode) { wchar_t* pMemUnicode = (wchar_t*)GlobalLock(hMemUnicode); if (pMemUnicode) { MultiByteToWideChar(CP_UTF8, 0, text, -1, pMemUnicode, wideLen); GlobalUnlock(hMemUnicode); } else { GlobalFree(hMemUnicode); hMemUnicode = NULL; } } }
// 클립보드 열기 (재시도) bool success = false; for (int retry = 0; retry < 3; retry++) { if (OpenClipboard(NULL)) { EmptyClipboard();
// ANSI 설정 bool ansiOk = (SetClipboardData(CF_TEXT, hMemAnsi) != NULL);
// 유니코드 설정 bool unicodeOk = true; if (hMemUnicode) { unicodeOk = (SetClipboardData(CF_UNICODETEXT, hMemUnicode) != NULL); }
CloseClipboard();
// 둘 중 하나라도 성공하면 OK if (ansiOk || unicodeOk) { success = true;
// 성공한 핸들은 클립보드가 소유 // 실패한 핸들만 해제 if (!ansiOk) { GlobalFree(hMemAnsi); } if (hMemUnicode && !unicodeOk) { GlobalFree(hMemUnicode); }
break; } }
Sleep(10); }
// 완전 실패 시 메모리 해제 if (!success) { GlobalFree(hMemAnsi); if (hMemUnicode) { GlobalFree(hMemUnicode); } return false; }
return true; } |