/** * \file cf_file.c * * \author myusgun * * \brief 파일 입출력 구현 */ #include "cf_file.h" #include "cf_local.h" #include "cf_error.h" #include #include #include #include #include #if defined(_WIN32) || defined(_WIN64) # include # include # define DELIMITER '\\' # define mkdir(a,b) _mkdir (a) # define access(a,b) _access (a,b) # define F_OK 0 # define W_OK 2 # define R_OK 4 /*------------------------------*/ # define S_IWUSR _S_IWRITE # define S_IRUSR _S_IREAD # define S_IXUSR _S_IEXEC /*------------------------------*/ # define S_IRGRP 0x00000000 # define S_IWGRP 0x00000000 # define S_IXGRP 0x00000000 /*------------------------------*/ # define S_IROTH 0x00000000 # define S_IWOTH 0x00000000 # define S_IXOTH 0x00000000 /*------------------------------*/ # define S_IRWXU 0x00000000 #else // #if defined(_WIN32) || defined(_WIN64) # include # define DELIMITER '/' # define O_BINARY 0x00000000 #endif // #if defined(_WIN32) || defined(_WIN64) #define ASSERT_CTX(__ctx) \ if (__ctx == NULL) \ return CF_ERROR_FILE_INVALID_CTX #define ASSERT_ARGS(x) \ if ((x)) \ return CF_ERROR_FILE_INVALID_ARGS typedef struct __cf_file_ctx__ { int fd; char path[1024]; CF_FILE_FLAG flag; } CF_FILE_CONTEXT; static int CF_File_Local_ConvertFlag (const CF_FILE_FLAG flag) { int result = 0; if (flag & CF_FILE_READ) result |= O_RDONLY; if (flag & CF_FILE_WRITE) result |= O_WRONLY; if (flag & CF_FILE_RW) result |= O_RDWR; if (flag & CF_FILE_CREATE) result |= O_CREAT; if (flag & CF_FILE_TRUNC) result |= O_TRUNC; if (flag & CF_FILE_APPEND) result |= O_APPEND; return result; } /** * 파일 열기 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 파일 컨텍스트 * \param path 파일 경로 * \param flag 파일 열기 플래그 * * \see CF_FILE_FLAG */ int CF_File_Open (cf_ctx * ctx, const char * path, const CF_FILE_FLAG flag) { int fd = 0; int result = 0; int osflag = 0; CF_FILE_CONTEXT * context = NULL; ASSERT_CTX (ctx); ASSERT_ARGS (path == NULL); TRY { context = NEWCTX (CF_FILE_CONTEXT); if (context == NULL) { result = CF_ERROR_FILE_CREATE_CTX; TRY_BREAK; } context->flag = flag; context->fd = -1; osflag = CF_File_Local_ConvertFlag (flag) | O_BINARY; #define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH) fd = open (path, osflag, FILE_MODE); if (fd < 0) { result = CF_ERROR_FILE_OPEN; TRY_BREAK; } context->fd = fd; snprintf (context->path, sizeof (context->path) - 1, "%s", path); *ctx = context; } CATCH_IF (result < 0) { CF_File_Close ((cf_ctx) context); } return result; } /** * 파일 닫기 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 파일 컨텍스트 */ int CF_File_Close (cf_ctx ctx) { int result = 0; int fd = 0; CF_FILE_CONTEXT * context = (CF_FILE_CONTEXT *) ctx; ASSERT_CTX (context); ASSERT_ARGS (context->fd < 0); fd = context->fd; free (context); result = close (fd); if (result < 0) return CF_ERROR_FILE_CLOSE; return CF_OK; } /** * 파일 읽기 * * \return 성공 시, 읽은 바이트 수; 실패 시, 오류 코드 * * \param ctx 파일 컨텍스트 * \param buf 데이터를 저장할 메모리 * \param len 데이터를 저장할 메모리의 크기 */ int CF_File_Read (const cf_ctx ctx, void * buf, const size_t len) { int result = 0; CF_FILE_CONTEXT * context = (CF_FILE_CONTEXT *) ctx; ASSERT_CTX (context); ASSERT_ARGS (context->fd < 0); ASSERT_ARGS (buf == NULL); result = (int) read (context->fd, buf, len); if (result < 0) return CF_ERROR_FILE_READ; return result; } /** * 파일 쓰기 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 파일 컨텍스트 * \param buf 데이터가 저장된 메모리 * \param len 쓸 데이터의 길이 */ int CF_File_Write (const cf_ctx ctx, const void * buf, const size_t len) { int result = 0; CF_FILE_CONTEXT * context = (CF_FILE_CONTEXT *) ctx; ASSERT_CTX (context); ASSERT_ARGS (context->fd < 0); ASSERT_ARGS (buf == NULL); result = (int) write (context->fd, buf, len); if (result != len) return CF_ERROR_FILE_WRITE; return CF_OK; } /** * 파일 크기 얻기 * * \return 성공 시, 파일 크기; 실패 시, 오류 코드 * * \param path 파일 경로 */ int CF_File_GetSize (const char * path) { int result = 0; CF_FILE_CONTEXT * context = NULL; ASSERT_ARGS (path == NULL); TRY { result = CF_File_Open ((cf_ctx *)&context, path, CF_FILE_READ); if (result < 0) { result = CF_ERROR_FILE_OPEN; TRY_BREAK; } result = (int) lseek (context->fd, 0, SEEK_END); if (result < 0) { result = CF_ERROR_FILE_GET_SIZE; TRY_BREAK; } } NO_CATCH; CF_File_Close ((cf_ctx)context); return result; } /** * 파일 및 디렉터리 존재 여부 검사 * * \return 존재 시, CF_TRUE; 아니면, CF_FALSE * * \param path 파일 및 디렉터리 경로 */ CF_BOOL CF_File_Exists (const char * path) { return (access (path, F_OK) == 0) ? CF_TRUE : CF_FALSE; } /** * 디렉터리 생성 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param path 생성할 디렉터리 경로 */ int CF_File_MakeDirectory (const char * path) { int result = 0; size_t length = 0; char * fullpath = NULL; char stepPath[256] = {0x00,}; char * f = NULL; char * d = NULL; ASSERT_ARGS (path == NULL); TRY { length = strlen (path); fullpath = (char *) calloc (length + 2, 1); if (!fullpath) { result = CF_ERROR_FILE_ALLOCATE_BUFFER; TRY_BREAK; } f = fullpath; d = stepPath; snprintf (fullpath, length + 1, "%s%c", path, DELIMITER); for (*d++ = *f++ ; *f ; *d++ = *f++) { if (*f != DELIMITER) continue; if (CF_File_Exists (stepPath)) continue; #define DIRECTORY_MODE (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) result = mkdir (stepPath, DIRECTORY_MODE); if (result && errno != EEXIST) { result = CF_ERROR_FILE_MAKE_DIRECTORY; break; } } } NO_CATCH; if (fullpath) free (fullpath); return result; } /** * 컨텍스트가 열고 있는 파일의 경로를 가져옴 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 파일 컨텍스트 * \param path 파일 경로를 저장할 충분한 공간의 메모리 */ int CF_File_GetPath (const cf_ctx ctx, char * path) { CF_FILE_CONTEXT * context = (CF_FILE_CONTEXT *) ctx; ASSERT_CTX (context); ASSERT_ARGS (path == NULL); snprintf (path, sizeof (context->path), "%s", context->path); return CF_OK; }