1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-22 18:26:39 +00:00

Minor fixes.

* The new logfile handler should now work properly.

* A UTF-8 BOM is now written at the start of every new logfile.
This commit is contained in:
Pablo Curiel 2021-03-08 07:11:28 -04:00
parent 3bc14696ec
commit 43f744326f
15 changed files with 228 additions and 120 deletions

18
.gitignore vendored
View file

@ -1,15 +1,15 @@
build
/*.elf
/*.nacp
/*.nro
/*.nso
/*.map
/*.pfs0
/*.lst
/*.tar.bz2
*.elf
*.nacp
*.nro
*.nso
*.map
*.pfs0
*.lst
*.tar.bz2
/code_templates/tmp/*
/source/main.c
/*.log
*.log
# Clion files
.idea

View file

@ -29,7 +29,7 @@ for f in ./code_templates/*.c; do
cp ./$filename.nro ./code_templates/tmp/$filename/nxdumptool-rewrite.nro
#cp ./$filename.elf ./code_templates/tmp/$filename/nxdumptool-rewrite.elf
rm -f ./build/main.o ./build/main.d ./build/utils.o ./build/utils.d ./build/usb.o ./build/usb.d ./$filename.*
rm -f ./build/main.o ./build/main.d ./build/utils.o ./build/utils.d ./build/usb.o ./build/usb.d ./build/log.o ./build/log.d ./$filename.*
done
make clean

View file

@ -31,6 +31,10 @@
#define BLOCK_SIZE 0x800000
#define OUTPATH "/nsp/"
int g_argc = 0;
char **g_argv = NULL;
const char *g_appLaunchPath = NULL;
static const char *dump_type_strings[] = {
"dump base application",
"dump update",
@ -748,8 +752,8 @@ end:
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
g_argc = argc;
g_argv = argv;
int ret = 0;

View file

@ -30,6 +30,10 @@
#define BLOCK_SIZE 0x800000
int g_argc = 0;
char **g_argv = NULL;
const char *g_appLaunchPath = NULL;
typedef struct
{
void *data;
@ -906,8 +910,8 @@ static void nspDump(TitleInfo *title_info)
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
g_argc = argc;
g_argv = argv;
int ret = 0;

View file

@ -25,6 +25,10 @@
#define BLOCK_SIZE 0x800000
int g_argc = 0;
char **g_argv = NULL;
const char *g_appLaunchPath = NULL;
static Mutex g_fileMutex = 0;
static CondVar g_readCondvar = 0, g_writeCondvar = 0;
@ -323,8 +327,8 @@ u8 get_program_id_offset(TitleInfo *info, u32 program_count)
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
g_argc = argc;
g_argv = argv;
int ret = 0;

View file

@ -27,6 +27,10 @@
#define BLOCK_SIZE 0x800000
#define OUTPATH "sdmc:/systitle_dumps"
int g_argc = 0;
char **g_argv = NULL;
const char *g_appLaunchPath = NULL;
static u8 *buf = NULL;
static FILE *filefd = NULL;
static char path[FS_MAX_PATH * 2] = {0};
@ -220,8 +224,8 @@ static void dumpFsSection(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx)
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
g_argc = argc;
g_argv = argv;
int ret = 0;

View file

@ -26,6 +26,10 @@
#define BLOCK_SIZE USB_TRANSFER_BUFFER_SIZE
int g_argc = 0;
char **g_argv = NULL;
const char *g_appLaunchPath = NULL;
/* Type definitions. */
typedef void (*MenuElementOptionFunction)(u32 idx);
@ -180,8 +184,8 @@ static char path[FS_MAX_PATH] = {0};
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
g_argc = argc;
g_argv = argv;
int ret = 0;

View file

@ -26,6 +26,10 @@
#define BLOCK_SIZE USB_TRANSFER_BUFFER_SIZE
int g_argc = 0;
char **g_argv = NULL;
const char *g_appLaunchPath = NULL;
static Mutex g_fileMutex = 0;
static CondVar g_readCondvar = 0, g_writeCondvar = 0;
@ -302,8 +306,8 @@ u8 get_program_id_offset(TitleInfo *info, u32 program_count)
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
g_argc = argc;
g_argv = argv;
int ret = 0;

View file

@ -26,6 +26,10 @@
#include "nacp.h"
#include "legal_info.h"
int g_argc = 0;
char **g_argv = NULL;
const char *g_appLaunchPath = NULL;
static void consolePrint(const char *text, ...)
{
va_list v;
@ -48,8 +52,8 @@ static void writeFile(void *buf, size_t buf_size, const char *path)
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
g_argc = argc;
g_argv = argv;
int ret = 0;

View file

@ -79,4 +79,11 @@ typedef struct {
};
} VersionType2;
/// These are set in main().
extern int g_argc;
extern char **g_argv;
/// This is set in utilsInitializeResources().
extern const char *g_appLaunchPath;
#endif /* __COMMON_H__ */

View file

@ -20,7 +20,7 @@
#include "utils.h"
#define LOG_FILE_PATH "./" APP_TITLE ".log"
#define LOG_FILE_NAME APP_TITLE ".log"
#define LOG_BUF_SIZE 0x400000 /* 4 MiB. */
#define LOG_FORCE_FLUSH 0 /* Forces a log buffer flush each time the logfile is written to. */
@ -34,19 +34,20 @@ static s64 g_logFileOffset = 0;
static char *g_logBuffer = NULL;
static size_t g_logBufferLength = 0;
static const char *g_utf8Bom = "\xEF\xBB\xBF";
static const char *g_logStrFormat = "[%d-%02d-%02d %02d:%02d:%02d.%lu] %s -> ";
static const char *g_logLineBreak = "\r\n";
/* Function prototypes. */
static bool logAllocateLogBuffer(void);
static bool logOpenLogFile(void);
static void _logFlushLogFile(bool lock);
static void _logWriteStringToLogFile(const char *src, bool lock);
static void _logWriteFormattedStringToLogFile(const char *func_name, const char *fmt, va_list args, bool lock);
static void _logFlushLogFile(bool lock);
static bool logAllocateLogBuffer(void);
static bool logOpenLogFile(void);
void logWriteStringToLogFile(const char *src)
{
_logWriteStringToLogFile(src, true);
@ -66,7 +67,8 @@ void logWriteFormattedStringToBuffer(char **dst, size_t *dst_size, const char *f
va_list args;
size_t str1_len = 0, str2_len = 0, log_str_len = 0;
int str1_len = 0, str2_len = 0;
size_t log_str_len = 0;
char *dst_ptr = *dst, *tmp_str = NULL;
size_t dst_cur_size = *dst_size, dst_str_len = (dst_ptr ? strlen(dst_ptr) : 0);
@ -86,12 +88,12 @@ void logWriteFormattedStringToBuffer(char **dst, size_t *dst_size, const char *f
/* Get formatted string length. */
str1_len = snprintf(NULL, 0, g_logStrFormat, ts->tm_year, ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, now.tv_nsec, func_name);
if ((int)str1_len <= 0) goto end;
if (str1_len <= 0) goto end;
str2_len = vsnprintf(NULL, 0, fmt, args);
if ((int)str2_len <= 0) goto end;
if (str2_len <= 0) goto end;
log_str_len = (str1_len + str2_len + 3);
log_str_len = (size_t)(str1_len + str2_len + 3);
if (!dst_cur_size || log_str_len > (dst_cur_size - dst_str_len))
{
@ -115,7 +117,7 @@ void logWriteFormattedStringToBuffer(char **dst, size_t *dst_size, const char *f
/* Generate formatted string. */
sprintf(dst_ptr + dst_str_len, g_logStrFormat, ts->tm_year, ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, now.tv_nsec, func_name);
vsprintf(dst_ptr + dst_str_len + str1_len, fmt, args);
vsprintf(dst_ptr + dst_str_len + (size_t)str1_len, fmt, args);
strcat(dst_ptr, g_logLineBreak);
end:
@ -198,58 +200,6 @@ void logControlMutex(bool lock)
}
}
static bool logAllocateLogBuffer(void)
{
if (g_logBuffer) return true;
g_logBuffer = memalign(LOG_BUF_SIZE, LOG_BUF_SIZE);
return (g_logBuffer != NULL);
}
static bool logOpenLogFile(void)
{
if (serviceIsActive(&(g_logFile.s))) return true;
Result rc = 0;
/* Get SD card FsFileSystem object. */
FsFileSystem *sdmc_fs = utilsGetSdCardFileSystemObject();
if (!sdmc_fs) return false;
/* Create file. This will fail if the logfile exists, so we don't check its return value. */
fsFsCreateFile(sdmc_fs, LOG_FILE_PATH, 0, 0);
/* Open file. */
rc = fsFsOpenFile(sdmc_fs, LOG_FILE_PATH, FsOpenMode_Write | FsOpenMode_Append, &g_logFile);
if (R_SUCCEEDED(rc))
{
/* Get file size. */
rc = fsFileGetSize(&g_logFile, &g_logFileOffset);
if (R_FAILED(rc)) fsFileClose(&g_logFile);
}
return R_SUCCEEDED(rc);
}
static void _logFlushLogFile(bool lock)
{
if (lock) mutexLock(&g_logMutex);
if (!serviceIsActive(&(g_logFile.s)) || !g_logBuffer || !g_logBufferLength) goto end;
/* Write log buffer contents and flush the written data right away. */
Result rc = fsFileWrite(&g_logFile, g_logFileOffset, g_logBuffer, g_logBufferLength, FsWriteOption_Flush);
if (R_SUCCEEDED(rc))
{
/* Update global variables. */
g_logFileOffset += (s64)g_logBufferLength;
*g_logBuffer = '\0';
g_logBufferLength = 0;
}
end:
if (lock) mutexUnlock(&g_logMutex);
}
static void _logWriteStringToLogFile(const char *src, bool lock)
{
if (!src || !*src) return;
@ -315,7 +265,9 @@ static void _logWriteFormattedStringToLogFile(const char *func_name, const char
if (lock) mutexLock(&g_logMutex);
Result rc = 0;
size_t str1_len = 0, str2_len = 0, log_str_len = 0;
int str1_len = 0, str2_len = 0;
size_t log_str_len = 0;
char *tmp_str = NULL;
size_t tmp_len = 0;
@ -334,12 +286,12 @@ static void _logWriteFormattedStringToLogFile(const char *func_name, const char
/* Get formatted string length. */
str1_len = snprintf(NULL, 0, g_logStrFormat, ts->tm_year, ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, now.tv_nsec, func_name);
if ((int)str1_len <= 0) goto end;
if (str1_len <= 0) goto end;
str2_len = vsnprintf(NULL, 0, fmt, args);
if ((int)str2_len <= 0) goto end;
if (str2_len <= 0) goto end;
log_str_len = (str1_len + str2_len + 2);
log_str_len = (size_t)(str1_len + str2_len + 2);
/* Check if the formatted string length is less than the log buffer size. */
if (log_str_len < LOG_BUF_SIZE)
@ -353,7 +305,7 @@ static void _logWriteFormattedStringToLogFile(const char *func_name, const char
/* Nice and easy string formatting using the log buffer. */
sprintf(g_logBuffer + g_logBufferLength, g_logStrFormat, ts->tm_year, ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, now.tv_nsec, func_name);
vsprintf(g_logBuffer + g_logBufferLength + str1_len, fmt, args);
vsprintf(g_logBuffer + g_logBufferLength + (size_t)str1_len, fmt, args);
strcat(g_logBuffer, g_logLineBreak);
g_logBufferLength += log_str_len;
} else {
@ -367,7 +319,7 @@ static void _logWriteFormattedStringToLogFile(const char *func_name, const char
/* Generate formatted string. */
sprintf(tmp_str, g_logStrFormat, ts->tm_year, ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, now.tv_nsec, func_name);
vsprintf(tmp_str + str1_len, fmt, args);
vsprintf(tmp_str + (size_t)str1_len, fmt, args);
strcat(tmp_str, g_logLineBreak);
/* Write formatted string data until it no longer exceeds the log buffer size. */
@ -399,3 +351,89 @@ end:
if (lock) mutexUnlock(&g_logMutex);
}
static void _logFlushLogFile(bool lock)
{
if (lock) mutexLock(&g_logMutex);
if (!serviceIsActive(&(g_logFile.s)) || !g_logBuffer || !g_logBufferLength) goto end;
/* Write log buffer contents and flush the written data right away. */
Result rc = fsFileWrite(&g_logFile, g_logFileOffset, g_logBuffer, g_logBufferLength, FsWriteOption_Flush);
if (R_SUCCEEDED(rc))
{
/* Update global variables. */
g_logFileOffset += (s64)g_logBufferLength;
*g_logBuffer = '\0';
g_logBufferLength = 0;
}
end:
if (lock) mutexUnlock(&g_logMutex);
}
static bool logAllocateLogBuffer(void)
{
if (g_logBuffer) return true;
g_logBuffer = memalign(LOG_BUF_SIZE, LOG_BUF_SIZE);
return (g_logBuffer != NULL);
}
static bool logOpenLogFile(void)
{
if (serviceIsActive(&(g_logFile.s))) return true;
Result rc = 0;
char path[FS_MAX_PATH] = {0}, *ptr1 = NULL, *ptr2 = NULL;
/* Get SD card FsFileSystem object. */
FsFileSystem *sdmc_fs = utilsGetSdCardFileSystemObject();
if (!sdmc_fs) return false;
/* Generate logfile path. */
if (g_appLaunchPath)
{
ptr1 = strchr(g_appLaunchPath, '/');
ptr2 = strrchr(g_appLaunchPath, '/');
if (ptr1 != ptr2)
{
/* Create logfile in the current working directory. */
snprintf(path, sizeof(path), "%.*s", (int)((ptr2 - ptr1) + 1), ptr1);
size_t path_len = strlen(path);
snprintf(path + path_len, sizeof(path) - path_len, LOG_FILE_NAME);
} else {
/* Create logfile in the SD card root directory. */
sprintf(path, "/" LOG_FILE_NAME);
}
} else {
/* Create logfile in the SD card root directory. */
sprintf(path, "/" LOG_FILE_NAME);
}
/* Create file. This will fail if the logfile exists, so we don't check its return value. */
fsFsCreateFile(sdmc_fs, path, 0, 0);
/* Open file. */
rc = fsFsOpenFile(sdmc_fs, path, FsOpenMode_Write | FsOpenMode_Append, &g_logFile);
if (R_SUCCEEDED(rc))
{
/* Get file size. */
rc = fsFileGetSize(&g_logFile, &g_logFileOffset);
if (R_SUCCEEDED(rc))
{
/* Write UTF-8 BOM right away (if needed). */
if (!g_logFileOffset)
{
size_t utf8_bom_len = strlen(g_utf8Bom);
fsFileWrite(&g_logFile, g_logFileOffset, g_utf8Bom, utf8_bom_len, FsWriteOption_Flush);
g_logFileOffset += (s64)utf8_bom_len;
}
} else {
fsFileClose(&g_logFile);
}
}
return R_SUCCEEDED(rc);
}

View file

@ -47,7 +47,8 @@ void logFlushLogFile(void);
/// Closes the logfile.
void logCloseLogFile(void);
/// (Un)locks the log mutex. Use with caution.
/// (Un)locks the log mutex. Can be used to block other threads and prevent them from writing data to the logfile.
/// Use with caution.
void logControlMutex(bool lock);
#endif /* __LOG_H__ */

View file

@ -547,12 +547,24 @@ static RomFileSystemDirectoryEntry *romfsGetChildDirectoryEntryByName(RomFileSys
size_t name_len = 0;
RomFileSystemDirectoryEntry *child_dir_entry = NULL;
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !dir_entry || (dir_offset = dir_entry->directory_offset) == ROMFS_VOID_ENTRY || !name || !(name_len = strlen(name))) return NULL;
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !dir_entry || (dir_offset = dir_entry->directory_offset) == ROMFS_VOID_ENTRY || !name || !(name_len = strlen(name)))
{
LOG_MSG("Invalid parameters!");
return NULL;
}
while(dir_offset != ROMFS_VOID_ENTRY)
{
if (!(child_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset))) return NULL;
if (!strcmp(child_dir_entry->name, name)) return child_dir_entry;
if (!(child_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)))
{
LOG_MSG("Failed to retrieve directory entry at offset 0x%lX!", dir_offset);
break;
}
/* strncmp() is used here instead of strcmp() because names stored in RomFS sections are not always NULL terminated. */
/* If the name ends at a 4-byte boundary, the next entry starts immediately. */
if (child_dir_entry->name_length == name_len && !strncmp(child_dir_entry->name, name, name_len)) return child_dir_entry;
dir_offset = child_dir_entry->next_offset;
}
@ -565,13 +577,25 @@ static RomFileSystemFileEntry *romfsGetChildFileEntryByName(RomFileSystemContext
size_t name_len = 0;
RomFileSystemFileEntry *child_file_entry = NULL;
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !ctx->file_table || !ctx->file_table_size || !dir_entry || (file_offset = dir_entry->file_offset) == ROMFS_VOID_ENTRY || !name || \
!(name_len = strlen(name))) return NULL;
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !ctx->file_table || !ctx->file_table_size || !dir_entry || (file_offset = dir_entry->file_offset) == ROMFS_VOID_ENTRY || \
!name || !(name_len = strlen(name)))
{
LOG_MSG("Invalid parameters!");
return NULL;
}
while(file_offset != ROMFS_VOID_ENTRY)
{
if (!(child_file_entry = romfsGetFileEntryByOffset(ctx, file_offset))) return NULL;
if (!strcmp(child_file_entry->name, name)) return child_file_entry;
if (!(child_file_entry = romfsGetFileEntryByOffset(ctx, file_offset)))
{
LOG_MSG("Failed to retrieve file entry at offset 0x%lX!", file_offset);
break;
}
/* strncmp() is used here instead of strcmp() because names stored in RomFS sections are not always NULL terminated. */
/* If the name ends at a 4-byte boundary, the next entry starts immediately. */
if (child_file_entry->name_length == name_len && !strncmp(child_file_entry->name, name, name_len)) return child_file_entry;
file_offset = child_file_entry->next_offset;
}

View file

@ -662,8 +662,7 @@ static bool usbSendCommand(void)
UsbStatus *cmd_status = (UsbStatus*)g_usbTransferBuffer;
u32 status = UsbStatusType_Success;
/* Log error message only if the USB session has been started, or if thread exit flag hasn't been enabled. */
bool ret = false, zlt_required = false, cmd_block_written = false, log_rw_errors = (g_usbSessionStarted || !g_usbDetectionThreadExitFlag);
bool ret = false, zlt_required = false, cmd_block_written = false;
if ((sizeof(UsbCommandHeader) + cmd_block_size) > USB_TRANSFER_BUFFER_SIZE)
{
@ -675,7 +674,7 @@ static bool usbSendCommand(void)
/* Write command header first. */
if (!usbWrite(cmd_header, sizeof(UsbCommandHeader)))
{
if (log_rw_errors) LOG_MSG("Failed to write header for type 0x%X command!", cmd);
LOG_MSG("Failed to write header for type 0x%X command!", cmd);
status = UsbStatusType_WriteCommandFailed;
goto end;
}
@ -694,7 +693,7 @@ static bool usbSendCommand(void)
cmd_block_written = usbWrite(g_usbTransferBuffer, cmd_block_size);
if (!cmd_block_written)
{
if (log_rw_errors) LOG_MSG("Failed to write command block for type 0x%X command!", cmd);
LOG_MSG("Failed to write command block for type 0x%X command!", cmd);
status = UsbStatusType_WriteCommandFailed;
}
@ -708,7 +707,7 @@ static bool usbSendCommand(void)
/* Read status block. */
if (!usbRead(cmd_status, sizeof(UsbStatus)))
{
if (log_rw_errors) LOG_MSG("Failed to read 0x%lX bytes long status block for type 0x%X command!", sizeof(UsbStatus), cmd);
LOG_MSG("Failed to read 0x%lX bytes long status block for type 0x%X command!", sizeof(UsbStatus), cmd);
status = UsbStatusType_ReadStatusFailed;
goto end;
}

View file

@ -82,9 +82,26 @@ bool utilsInitializeResources(void)
padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl);
padInitializeWithMask(&g_padState, 0x1000000FFUL);
/* Retrieve pointer to the application launch path. */
if (g_argc && g_argv)
{
for(int i = 0; i < g_argc; i++)
{
if (g_argv[i] && !strncmp(g_argv[i], "sdmc:/", 6))
{
g_appLaunchPath = (const char*)g_argv[i];
break;
}
}
}
/* Retrieve pointer to the SD card FsFileSystem element. */
if (!(g_sdCardFileSystem = fsdevGetDeviceFileSystem("sdmc:"))) goto end;
/* Create logfile. */
logWriteStringToLogFile("________________________________________________________________\r\n");
LOG_MSG(APP_TITLE " v%u.%u.%u starting. Built on " __DATE__ " - " __TIME__ ".", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO);
if (g_appLaunchPath) LOG_MSG("Launch path: \"%s\".", g_appLaunchPath);
/* Log Horizon OS version. */
u32 hos_version = hosversionGet();
@ -150,13 +167,6 @@ bool utilsInitializeResources(void)
goto end;
}
/* Retrieve pointer to the SD card FsFileSystem element. */
if (!(g_sdCardFileSystem = fsdevGetDeviceFileSystem("sdmc:")))
{
LOG_MSG("Failed to retrieve FsFileSystem from SD card!");
goto end;
}
/* Mount eMMC BIS System partition. */
if (!utilsMountEmmcBisSystemPartitionStorage()) goto end;
@ -365,14 +375,15 @@ bool utilsAppendFormattedStringToBuffer(char **dst, size_t *dst_size, const char
va_list args;
size_t formatted_str_len = 0;
int formatted_str_len = 0;
size_t formatted_str_len_cast = 0;
char *dst_ptr = *dst, *tmp_str = NULL;
size_t dst_cur_size = *dst_size, dst_str_len = (dst_ptr ? strlen(dst_ptr) : 0);
bool success = false;
if (dst_str_len >= dst_cur_size)
if (dst_cur_size && dst_str_len >= dst_cur_size)
{
LOG_MSG("String length is equal to or greater than the provided buffer size! (0x%lX >= 0x%lX).", dst_str_len, dst_cur_size);
return false;
@ -382,18 +393,18 @@ bool utilsAppendFormattedStringToBuffer(char **dst, size_t *dst_size, const char
/* Get formatted string length. */
formatted_str_len = vsnprintf(NULL, 0, fmt, args);
if ((int)formatted_str_len <= 0)
if (formatted_str_len <= 0)
{
LOG_MSG("Failed to retrieve formatted string length!");
goto end;
}
formatted_str_len++;
formatted_str_len_cast = (size_t)(formatted_str_len + 1);
if (!dst_cur_size || formatted_str_len > (dst_cur_size - dst_str_len))
if (!dst_cur_size || formatted_str_len_cast > (dst_cur_size - dst_str_len))
{
/* Update buffer size. */
dst_cur_size = (dst_str_len + formatted_str_len);
dst_cur_size = (dst_str_len + formatted_str_len_cast);
/* Reallocate buffer. */
tmp_str = realloc(dst_ptr, dst_cur_size);
@ -407,7 +418,7 @@ bool utilsAppendFormattedStringToBuffer(char **dst, size_t *dst_size, const char
tmp_str = NULL;
/* Clear allocated area. */
memset(dst_ptr + dst_str_len, 0, formatted_str_len);
memset(dst_ptr + dst_str_len, 0, formatted_str_len_cast);
/* Update pointers. */
*dst = dst_ptr;