Fix masterKeyProvider args & mem bugs + multiple format extensions.
This commit is contained in:
parent
070f909a7f
commit
dd123a431c
@ -185,6 +185,7 @@ void cli_save(Arguments *args, Operation *operation);
|
|||||||
|
|
||||||
MPMasterKeyProvider cli_masterKeyProvider_op(Operation *operation);
|
MPMasterKeyProvider cli_masterKeyProvider_op(Operation *operation);
|
||||||
MPMasterKeyProvider cli_masterKeyProvider_str(const char *masterPassword);
|
MPMasterKeyProvider cli_masterKeyProvider_str(const char *masterPassword);
|
||||||
|
void cli_masterKeyProvider_free();
|
||||||
|
|
||||||
/** ========================================================================
|
/** ========================================================================
|
||||||
* MAIN */
|
* MAIN */
|
||||||
@ -274,6 +275,7 @@ void cli_free(Arguments *args, Operation *operation) {
|
|||||||
mpw_marshal_free( &operation->user );
|
mpw_marshal_free( &operation->user );
|
||||||
operation->site = NULL;
|
operation->site = NULL;
|
||||||
operation->question = NULL;
|
operation->question = NULL;
|
||||||
|
cli_masterKeyProvider_free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,21 +459,29 @@ void cli_user(Arguments *args, Operation *operation) {
|
|||||||
|
|
||||||
// Find mpsites file from parameters.
|
// Find mpsites file from parameters.
|
||||||
FILE *sitesFile = NULL;
|
FILE *sitesFile = NULL;
|
||||||
|
const char **extensions = NULL;
|
||||||
|
int count = mpw_marshal_format_extensions( operation->sitesFormat, &extensions );
|
||||||
|
for (int e = 0; !sitesFile && e < count; ++e) {
|
||||||
mpw_free_string( &operation->sitesPath );
|
mpw_free_string( &operation->sitesPath );
|
||||||
operation->sitesPath = mpw_path( operation->fullName, mpw_marshal_format_extension( operation->sitesFormat ) );
|
operation->sitesPath = mpw_path( operation->fullName, extensions[e] );
|
||||||
if (!operation->sitesPath || !(sitesFile = fopen( operation->sitesPath, "r" ))) {
|
|
||||||
dbg( "Couldn't open configuration file:\n %s: %s", operation->sitesPath, strerror( errno ) );
|
|
||||||
|
|
||||||
// Try to fall back to the flat format.
|
if (!operation->sitesPath || !(sitesFile = fopen( operation->sitesPath, "r" )))
|
||||||
if (!operation->sitesFormatFixed) {
|
dbg( "Couldn't open configuration file:\n %s: %s", operation->sitesPath, strerror( errno ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sitesFile && !operation->sitesFormatFixed)
|
||||||
|
for (MPMarshalFormat format = MPMarshalFormatFirst; !sitesFile && format <= MPMarshalFormatLast; ++format) {
|
||||||
|
count = mpw_marshal_format_extensions( operation->sitesFormat, &extensions );
|
||||||
|
|
||||||
|
for (int e = 0; !sitesFile && e < count; ++e) {
|
||||||
mpw_free_string( &operation->sitesPath );
|
mpw_free_string( &operation->sitesPath );
|
||||||
operation->sitesPath = mpw_path( operation->fullName, mpw_marshal_format_extension( MPMarshalFormatFlat ) );
|
operation->sitesPath = mpw_path( operation->fullName, extensions[e] );
|
||||||
if (operation->sitesPath && (sitesFile = fopen( operation->sitesPath, "r" )))
|
|
||||||
operation->sitesFormat = MPMarshalFormatFlat;
|
if (!operation->sitesPath || !(sitesFile = fopen( operation->sitesPath, "r" )))
|
||||||
else
|
|
||||||
dbg( "Couldn't open configuration file:\n %s: %s", operation->sitesPath, strerror( errno ) );
|
dbg( "Couldn't open configuration file:\n %s: %s", operation->sitesPath, strerror( errno ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
free( extensions );
|
||||||
|
|
||||||
// Load the user object from mpsites.
|
// Load the user object from mpsites.
|
||||||
if (!sitesFile)
|
if (!sitesFile)
|
||||||
@ -490,7 +500,8 @@ void cli_user(Arguments *args, Operation *operation) {
|
|||||||
MPMarshalError marshalError = { .type = MPMarshalSuccess };
|
MPMarshalError marshalError = { .type = MPMarshalSuccess };
|
||||||
mpw_marshal_info_free( &sitesInputInfo );
|
mpw_marshal_info_free( &sitesInputInfo );
|
||||||
mpw_marshal_free( &operation->user );
|
mpw_marshal_free( &operation->user );
|
||||||
operation->user = mpw_marshal_read( sitesInputData, sitesInputFormat, cli_masterKeyProvider_op( operation ), &marshalError );
|
operation->user = mpw_marshal_read( sitesInputData, sitesInputFormat,
|
||||||
|
cli_masterKeyProvider_op( operation ), &marshalError );
|
||||||
if (marshalError.type == MPMarshalErrorMasterPassword && operation->allowPasswordUpdate) {
|
if (marshalError.type == MPMarshalErrorMasterPassword && operation->allowPasswordUpdate) {
|
||||||
// Update master password in mpsites.
|
// Update master password in mpsites.
|
||||||
while (marshalError.type == MPMarshalErrorMasterPassword) {
|
while (marshalError.type == MPMarshalErrorMasterPassword) {
|
||||||
@ -498,14 +509,16 @@ void cli_user(Arguments *args, Operation *operation) {
|
|||||||
inf( "To update the configuration with this new master password, first confirm the old master password." );
|
inf( "To update the configuration with this new master password, first confirm the old master password." );
|
||||||
|
|
||||||
const char *importMasterPassword = NULL;
|
const char *importMasterPassword = NULL;
|
||||||
while (!importMasterPassword || !strlen( importMasterPassword ))
|
while (!importMasterPassword || !strlen( importMasterPassword )) {
|
||||||
|
mpw_free_string( &importMasterPassword );
|
||||||
importMasterPassword = mpw_getpass( "Old master password: " );
|
importMasterPassword = mpw_getpass( "Old master password: " );
|
||||||
|
}
|
||||||
|
|
||||||
mpw_marshal_free( &operation->user );
|
mpw_marshal_free( &operation->user );
|
||||||
operation->user = mpw_marshal_read( sitesInputData, sitesInputFormat, cli_masterKeyProvider_str( importMasterPassword ), &marshalError );
|
operation->user = mpw_marshal_read( sitesInputData, sitesInputFormat,
|
||||||
mpw_free_string( &importMasterPassword );
|
cli_masterKeyProvider_str( importMasterPassword ), &marshalError );
|
||||||
|
|
||||||
operation->user->masterKeyProvider = cli_masterKeyProvider_op( operation );
|
operation->user->masterKeyProvider = cli_masterKeyProvider_op( operation );
|
||||||
|
mpw_free_string( &importMasterPassword );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mpw_free_string( &sitesInputData );
|
mpw_free_string( &sitesInputData );
|
||||||
@ -772,15 +785,17 @@ void cli_mpw(Arguments *args, Operation *operation) {
|
|||||||
|
|
||||||
void cli_save(Arguments *args, Operation *operation) {
|
void cli_save(Arguments *args, Operation *operation) {
|
||||||
|
|
||||||
if (operation->sitesFormat == MPMarshalFormatNone)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!operation->sitesFormatFixed)
|
if (!operation->sitesFormatFixed)
|
||||||
operation->sitesFormat = MPMarshalFormatDefault;
|
operation->sitesFormat = MPMarshalFormatDefault;
|
||||||
|
|
||||||
|
const char **extensions = NULL;
|
||||||
|
if (!mpw_marshal_format_extensions( operation->sitesFormat, &extensions ))
|
||||||
|
return;
|
||||||
|
|
||||||
mpw_free_string( &operation->sitesPath );
|
mpw_free_string( &operation->sitesPath );
|
||||||
operation->sitesPath = mpw_path( operation->user->fullName, mpw_marshal_format_extension( operation->sitesFormat ) );
|
operation->sitesPath = mpw_path( operation->user->fullName, extensions[0] );
|
||||||
dbg( "Updating: %s (%s)", operation->sitesPath, mpw_nameForFormat( operation->sitesFormat ) );
|
dbg( "Updating: %s (%s)", operation->sitesPath, mpw_format_name( operation->sitesFormat ) );
|
||||||
|
free( extensions );
|
||||||
|
|
||||||
FILE *sitesFile = NULL;
|
FILE *sitesFile = NULL;
|
||||||
if (!operation->sitesPath || !mpw_mkdirs( operation->sitesPath ) || !(sitesFile = fopen( operation->sitesPath, "w" ))) {
|
if (!operation->sitesPath || !mpw_mkdirs( operation->sitesPath ) || !(sitesFile = fopen( operation->sitesPath, "w" ))) {
|
||||||
@ -800,31 +815,48 @@ void cli_save(Arguments *args, Operation *operation) {
|
|||||||
fclose( sitesFile );
|
fclose( sitesFile );
|
||||||
}
|
}
|
||||||
|
|
||||||
static MPMasterKey __cli_masterKeyProvider_currentKey;
|
static MPMasterKey __cli_masterKeyProvider_currentKey = NULL;
|
||||||
static MPAlgorithmVersion __cli_masterKeyProvider_currentAlgorithm;
|
static MPAlgorithmVersion __cli_masterKeyProvider_currentAlgorithm = (MPAlgorithmVersion)-1;
|
||||||
static const char *__cli_masterKeyProvider_currentPassword;
|
static const char *__cli_masterKeyProvider_currentPassword = NULL;
|
||||||
static Operation *__cli_masterKeyProvider_currentOperation;
|
static Operation *__cli_masterKeyProvider_currentOperation = NULL;
|
||||||
|
|
||||||
static MPMasterKey __cli_masterKeyProvider_op(MPAlgorithmVersion algorithm, const char *fullName) {
|
static MPMasterKey __cli_masterKeyProvider_op(MPAlgorithmVersion algorithm, const char *fullName) {
|
||||||
if (mpw_update_masterKey(&__cli_masterKeyProvider_currentKey, &__cli_masterKeyProvider_currentAlgorithm,
|
|
||||||
algorithm, fullName, __cli_masterKeyProvider_currentOperation->masterPassword))
|
if (mpw_update_master_key( &__cli_masterKeyProvider_currentKey, &__cli_masterKeyProvider_currentAlgorithm,
|
||||||
return __cli_masterKeyProvider_currentKey;
|
algorithm, fullName, __cli_masterKeyProvider_currentOperation->masterPassword ))
|
||||||
|
return mpw_memdup( __cli_masterKeyProvider_currentKey, MPMasterKeySize );
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static MPMasterKey __cli_masterKeyProvider_str(MPAlgorithmVersion algorithm, const char *fullName) {
|
static MPMasterKey __cli_masterKeyProvider_str(MPAlgorithmVersion algorithm, const char *fullName) {
|
||||||
if (mpw_update_masterKey(&__cli_masterKeyProvider_currentKey, &__cli_masterKeyProvider_currentAlgorithm,
|
|
||||||
algorithm, fullName, __cli_masterKeyProvider_currentPassword))
|
if (mpw_update_master_key( &__cli_masterKeyProvider_currentKey, &__cli_masterKeyProvider_currentAlgorithm,
|
||||||
return __cli_masterKeyProvider_currentKey;
|
algorithm, fullName, __cli_masterKeyProvider_currentPassword ))
|
||||||
|
return mpw_memdup( __cli_masterKeyProvider_currentKey, MPMasterKeySize );
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MPMasterKeyProvider cli_masterKeyProvider_op(Operation *operation) {
|
MPMasterKeyProvider cli_masterKeyProvider_op(Operation *operation) {
|
||||||
|
|
||||||
|
mpw_free_string( &__cli_masterKeyProvider_currentPassword );
|
||||||
__cli_masterKeyProvider_currentOperation = operation;
|
__cli_masterKeyProvider_currentOperation = operation;
|
||||||
return __cli_masterKeyProvider_op;
|
return __cli_masterKeyProvider_op;
|
||||||
}
|
}
|
||||||
|
|
||||||
MPMasterKeyProvider cli_masterKeyProvider_str(const char *masterPassword) {
|
MPMasterKeyProvider cli_masterKeyProvider_str(const char *masterPassword) {
|
||||||
__cli_masterKeyProvider_currentPassword = masterPassword;
|
|
||||||
|
mpw_free_string( &__cli_masterKeyProvider_currentPassword );
|
||||||
|
__cli_masterKeyProvider_currentPassword = mpw_strdup( masterPassword );
|
||||||
return __cli_masterKeyProvider_str;
|
return __cli_masterKeyProvider_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cli_masterKeyProvider_free() {
|
||||||
|
|
||||||
|
mpw_free( &__cli_masterKeyProvider_currentKey, MPMasterKeySize );
|
||||||
|
__cli_masterKeyProvider_currentAlgorithm = (MPAlgorithmVersion)-1;
|
||||||
|
mpw_free_string( &__cli_masterKeyProvider_currentPassword );
|
||||||
|
__cli_masterKeyProvider_currentOperation = NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -181,7 +181,7 @@ static bool mpw_marshal_write_flat(
|
|||||||
if (!user->redacted) {
|
if (!user->redacted) {
|
||||||
// Clear Text
|
// Clear Text
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
if (!(masterKey = user->masterKeyProvider( user->algorithm, user->fullName ))) {
|
if (!(masterKey = user->masterKeyProvider( site->algorithm, user->fullName ))) {
|
||||||
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -212,6 +212,7 @@ static bool mpw_marshal_write_flat(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if MPW_JSON
|
#if MPW_JSON
|
||||||
|
|
||||||
static bool mpw_marshal_write_json(
|
static bool mpw_marshal_write_json(
|
||||||
char **out, const MPMarshalledUser *user, MPMarshalError *error) {
|
char **out, const MPMarshalledUser *user, MPMarshalError *error) {
|
||||||
|
|
||||||
@ -263,7 +264,7 @@ static bool mpw_marshal_write_json(
|
|||||||
if (!user->redacted) {
|
if (!user->redacted) {
|
||||||
// Clear Text
|
// Clear Text
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
if (!(masterKey = user->masterKeyProvider( user->algorithm, user->fullName ))) {
|
if (!(masterKey = user->masterKeyProvider( site->algorithm, user->fullName ))) {
|
||||||
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -339,6 +340,7 @@ static bool mpw_marshal_write_json(
|
|||||||
*error = (MPMarshalError){ .type = MPMarshalSuccess };
|
*error = (MPMarshalError){ .type = MPMarshalSuccess };
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool mpw_marshal_write(
|
bool mpw_marshal_write(
|
||||||
@ -593,7 +595,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
|
|||||||
if (!user->redacted) {
|
if (!user->redacted) {
|
||||||
// Clear Text
|
// Clear Text
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
if (!(masterKey = masterKeyProvider( user->algorithm, user->fullName ))) {
|
if (!(masterKey = masterKeyProvider( site->algorithm, user->fullName ))) {
|
||||||
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -632,6 +634,7 @@ static MPMarshalledUser *mpw_marshal_read_flat(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if MPW_JSON
|
#if MPW_JSON
|
||||||
|
|
||||||
static void mpw_marshal_read_json_info(
|
static void mpw_marshal_read_json_info(
|
||||||
const char *in, MPMarshalInfo *info) {
|
const char *in, MPMarshalInfo *info) {
|
||||||
|
|
||||||
@ -713,7 +716,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
|
|||||||
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing value for full name." };
|
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing value for full name." };
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (!(masterKey = masterKeyProvider( user->algorithm, user->fullName ))) {
|
if (!(masterKey = masterKeyProvider( algorithm, fullName ))) {
|
||||||
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -779,7 +782,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
|
|||||||
if (!user->redacted) {
|
if (!user->redacted) {
|
||||||
// Clear Text
|
// Clear Text
|
||||||
mpw_free( &masterKey, MPMasterKeySize );
|
mpw_free( &masterKey, MPMasterKeySize );
|
||||||
if (!(masterKey = masterKeyProvider( user->algorithm, user->fullName ))) {
|
if (!(masterKey = masterKeyProvider( site->algorithm, user->fullName ))) {
|
||||||
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -827,6 +830,7 @@ static MPMarshalledUser *mpw_marshal_read_json(
|
|||||||
*error = (MPMarshalError){ .type = MPMarshalSuccess };
|
*error = (MPMarshalError){ .type = MPMarshalSuccess };
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MPMarshalInfo *mpw_marshal_read_info(
|
MPMarshalInfo *mpw_marshal_read_info(
|
||||||
@ -913,10 +917,41 @@ const char *mpw_marshal_format_extension(
|
|||||||
case MPMarshalFormatFlat:
|
case MPMarshalFormatFlat:
|
||||||
return "mpsites";
|
return "mpsites";
|
||||||
case MPMarshalFormatJSON:
|
case MPMarshalFormatJSON:
|
||||||
return "mpsites.json";
|
return "mpjson";
|
||||||
default: {
|
default: {
|
||||||
dbg( "Unknown format: %d", format );
|
dbg( "Unknown format: %d", format );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mpw_marshal_format_extensions(
|
||||||
|
const MPMarshalFormat format, const char ***extensions) {
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
switch (format) {
|
||||||
|
case MPMarshalFormatNone: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MPMarshalFormatFlat: {
|
||||||
|
const char *array[3] = { mpw_marshal_format_extension( format ), "mpsites.txt", "txt" };
|
||||||
|
count = sizeof( array ) / sizeof( *array );
|
||||||
|
*extensions = realloc( *extensions, count * sizeof( const char * ) );
|
||||||
|
memcpy( *extensions, array, count * sizeof( const char * ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MPMarshalFormatJSON: {
|
||||||
|
const char *array[3] = { mpw_marshal_format_extension( format ), "mpsites.json", "json" };
|
||||||
|
count = sizeof( array ) / sizeof( *array );
|
||||||
|
*extensions = realloc( *extensions, sizeof( array ) );
|
||||||
|
memcpy( *extensions, array, sizeof( array ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
dbg( "Unknown format: %d", format );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
@ -38,6 +38,8 @@ typedef mpw_enum( unsigned int, MPMarshalFormat ) {
|
|||||||
#else
|
#else
|
||||||
MPMarshalFormatDefault = MPMarshalFormatFlat,
|
MPMarshalFormatDefault = MPMarshalFormatFlat,
|
||||||
#endif
|
#endif
|
||||||
|
MPMarshalFormatFirst = MPMarshalFormatFlat,
|
||||||
|
MPMarshalFormatLast = MPMarshalFormatJSON,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef mpw_enum( unsigned int, MPMarshalErrorType ) {
|
typedef mpw_enum( unsigned int, MPMarshalErrorType ) {
|
||||||
@ -162,5 +164,12 @@ const char *mpw_nameForFormat(
|
|||||||
*/
|
*/
|
||||||
const char *mpw_marshal_format_extension(
|
const char *mpw_marshal_format_extension(
|
||||||
const MPMarshalFormat format);
|
const MPMarshalFormat format);
|
||||||
|
/**
|
||||||
|
* @param extensions An array of filename extensions that are used for files of this format,
|
||||||
|
* the first being the currently preferred/output extension. Free after use.
|
||||||
|
* @return The amount of filename extensions returned in the array.
|
||||||
|
*/
|
||||||
|
int mpw_marshal_format_extensions(
|
||||||
|
const MPMarshalFormat format, const char ***extensions);
|
||||||
|
|
||||||
#endif // _MPW_MARSHAL_H
|
#endif // _MPW_MARSHAL_H
|
||||||
|
@ -490,17 +490,25 @@ const size_t mpw_utf8_strlen(const char *utf8String) {
|
|||||||
return charlen;
|
return charlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *mpw_memdup(const void *src, size_t len) {
|
||||||
|
|
||||||
|
if (!src)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char *dst = malloc( len );
|
||||||
|
if (dst)
|
||||||
|
memcpy( dst, src, len );
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
char *mpw_strdup(const char *src) {
|
char *mpw_strdup(const char *src) {
|
||||||
|
|
||||||
if (!src)
|
if (!src)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
size_t len = strlen( src );
|
size_t len = strlen( src );
|
||||||
char *dst = malloc( len + 1 );
|
return mpw_memdup( src, len + 1 );
|
||||||
memcpy( dst, src, len );
|
|
||||||
dst[len] = '\0';
|
|
||||||
|
|
||||||
return dst;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *mpw_strndup(const char *src, size_t max) {
|
char *mpw_strndup(const char *src, size_t max) {
|
||||||
|
@ -223,6 +223,8 @@ bool mpw_id_buf_equals(const char *id1, const char *id2);
|
|||||||
|
|
||||||
/** @return The amount of display characters in the given UTF-8 string. */
|
/** @return The amount of display characters in the given UTF-8 string. */
|
||||||
const size_t mpw_utf8_strlen(const char *utf8String);
|
const size_t mpw_utf8_strlen(const char *utf8String);
|
||||||
|
/** Drop-in for memdup(3). */
|
||||||
|
void *mpw_memdup(const void *src, size_t len);
|
||||||
/** Drop-in for POSIX strdup(3). */
|
/** Drop-in for POSIX strdup(3). */
|
||||||
char *mpw_strdup(const char *src);
|
char *mpw_strdup(const char *src);
|
||||||
/** Drop-in for POSIX strndup(3). */
|
/** Drop-in for POSIX strndup(3). */
|
||||||
|
Loading…
Reference in New Issue
Block a user