#ifndef FILE_H #define FILE_H #include #include #define CONCAT(a, b) CONCAT_INNER(a, b) #define CONCAT_INNER(a, b) a ## b #define UNIQUE_NAME(base) CONCAT(base, __COUNTER__) enum file_err { FILE_ERR_OK, FILE_ERR_FAIL_OPEN, FILE_ERR_FAIL_SEEK, FILE_ERR_FAIL_READ, FILE_ERR_FAIL_WRITE, FILE_ERR_FAIL_CALLOC, FILE_ERR_FAIL_CLOSE, FILE_ERR_FILE_EMPTY, FILE_ERR_FAIL_WROTE_MORE, FILE_ERR_EMPTY }; uint8_t *file_read_all(const char *filepath, size_t *ret_size, enum file_err *ret_err); void file_save(const char *filepath, const char *str, size_t str_size, enum file_err *ret_err); void file_err_set(enum file_err *err, enum file_err err_); const char *file_err_to_cstr(enum file_err err); #if defined(BMP_IMP) || defined(IMP) #include #include #include #include uint8_t * file_read_all(const char *filepath, size_t *ret_size, enum file_err *ret_err) { int32_t fd = -1; off_t file_size = -1; size_t buf_size = 0; uint8_t *buf = NULL; fd = open(filepath, O_RDONLY); if ( fd < 0 ) { file_err_set(ret_err, FILE_ERR_FAIL_OPEN); return NULL; } file_size = lseek(fd, 0, SEEK_END); if ( file_size < 0 ) { file_err_set(ret_err, FILE_ERR_FAIL_SEEK); close(fd); return NULL; } lseek(fd, 0, SEEK_SET); if ( file_size == 0 ) { file_err_set(ret_err, FILE_ERR_EMPTY); close(fd); return NULL; } buf_size = ((size_t)file_size) + 1; buf = calloc(buf_size, sizeof(uint8_t)); if ( buf == NULL ) { file_err_set(ret_err, FILE_ERR_FAIL_CALLOC); close(fd); return NULL; } { ssize_t rd = read(fd, buf, (size_t)file_size); if ( rd < 0 ) { file_err_set(ret_err, FILE_ERR_FAIL_READ); close(fd); free(buf); return NULL; } if ( rd == 0 ) { file_err_set(ret_err, FILE_ERR_FILE_EMPTY); close(fd); free(buf); return NULL; } } if ( close(fd) != 0 ) { /* It should be possible to handle EIO */ file_err_set(ret_err, FILE_ERR_FAIL_CLOSE); close(fd); return NULL; } if ( ret_size != NULL ) { *ret_size = buf_size; } file_err_set(ret_err, FILE_ERR_OK); return buf; } void file_save(const char *filepath, const char *str, size_t str_size, enum file_err *ret_err) { ssize_t wrote = -1; int32_t fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if ( fd < 0 ) { file_err_set(ret_err, FILE_ERR_FAIL_OPEN); return; } wrote = write(fd, str, str_size); if ( wrote == -1 ) { file_err_set(ret_err, FILE_ERR_FAIL_WRITE); return; } if ( ((size_t) wrote) != str_size ) { file_err_set(ret_err, FILE_ERR_FAIL_WROTE_MORE); return; } if ( close(fd) != 0 ) { /* It should be possible to handle EIO */ file_err_set(ret_err, FILE_ERR_FAIL_CLOSE); close(fd); return; } file_err_set(ret_err, FILE_ERR_OK); } void file_err_set(enum file_err *err, enum file_err err_) { if ( err != NULL ) { *err = err_; } } const char * file_err_to_cstr(enum file_err err) { switch ( err ) { case FILE_ERR_OK: return "FILE_ERR_OK"; break; case FILE_ERR_FAIL_OPEN: return "FILE_ERR_FAIL_OPEN"; break; case FILE_ERR_FAIL_SEEK: return "FILE_ERR_FAIL_SEEK"; break; case FILE_ERR_FAIL_READ: return "FILE_ERR_FAIL_READ"; break; case FILE_ERR_FAIL_WRITE: return "FILE_ERR_FAIL_WRITE"; break; case FILE_ERR_FAIL_CALLOC: return "FILE_ERR_FAIL_CALLOC"; break; case FILE_ERR_FAIL_CLOSE: return "FILE_ERR_FAIL_CLOSE"; break; case FILE_ERR_FILE_EMPTY: return "FILE_ERR_FILE_EMPTY"; break; case FILE_ERR_FAIL_WROTE_MORE: return "FILE_ERR_FAIL_WROTE_MORE"; break; case FILE_ERR_EMPTY: return "FILE_ERR_EMPTY"; break; } return "FILE ERR INVALID"; } #endif #endif