From 568401a61266487e10cb2eb800eba833e127dc00 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Thu, 31 Aug 2017 15:30:42 -0400 Subject: [PATCH] Extract cli utilities into mpw-cli-util to keep things cleaner. --- core/c/mpw-util.h | 15 +- .../project.pbxproj | 6 + platform-independent/cli-c/build | 2 +- platform-independent/cli-c/cli/mpw-cli-util.c | 244 ++++++++++++++++++ platform-independent/cli-c/cli/mpw-cli-util.h | 64 +++++ platform-independent/cli-c/cli/mpw-cli.c | 236 +---------------- 6 files changed, 325 insertions(+), 242 deletions(-) create mode 100644 platform-independent/cli-c/cli/mpw-cli-util.c create mode 100644 platform-independent/cli-c/cli/mpw-cli-util.h diff --git a/core/c/mpw-util.h b/core/c/mpw-util.h index f26d8a29..896ac215 100644 --- a/core/c/mpw-util.h +++ b/core/c/mpw-util.h @@ -31,42 +31,42 @@ extern int mpw_verbosity; #define trc_level 3 /** Logging internal state. */ #define trc(...) ({ \ - if (mpw_verbosity >= 3) \ + if (mpw_verbosity >= trc_level) \ fprintf( stderr, __VA_ARGS__ ); }) #endif #ifndef dbg #define dbg_level 2 /** Logging state and events interesting when investigating issues. */ #define dbg(...) ({ \ - if (mpw_verbosity >= 2) \ + if (mpw_verbosity >= dbg_level) \ fprintf( stderr, __VA_ARGS__ ); }) #endif #ifndef inf #define inf_level 1 /** User messages. */ #define inf(...) ({ \ - if (mpw_verbosity >= 1) \ + if (mpw_verbosity >= inf_level) \ fprintf( stderr, __VA_ARGS__ ); }) #endif #ifndef wrn #define wrn_level 0 /** Recoverable issues and user suggestions. */ #define wrn(...) ({ \ - if (mpw_verbosity >= 0) \ + if (mpw_verbosity >= wrn_level) \ fprintf( stderr, __VA_ARGS__ ); }) #endif #ifndef err #define err_level -1 /** Unrecoverable issues. */ #define err(...) ({ \ - if (mpw_verbosity >= -1) \ + if (mpw_verbosity >= err_level) \ fprintf( stderr, __VA_ARGS__ ); }) #endif #ifndef ftl #define ftl_level -2 /** Issues that lead to abortion. */ #define ftl(...) ({ \ - if (mpw_verbosity >= -2) \ + if (mpw_verbosity >= ftl_level) \ fprintf( stderr, __VA_ARGS__ ); }) #endif @@ -85,6 +85,9 @@ extern int mpw_verbosity; #ifndef ERR #define ERR -1 #endif +#ifndef OK +#define OK 0 +#endif #ifndef stringify #define stringify(s) #s #endif diff --git a/platform-darwin/MasterPassword-macOS.xcodeproj/project.pbxproj b/platform-darwin/MasterPassword-macOS.xcodeproj/project.pbxproj index c10677f5..264455f0 100644 --- a/platform-darwin/MasterPassword-macOS.xcodeproj/project.pbxproj +++ b/platform-darwin/MasterPassword-macOS.xcodeproj/project.pbxproj @@ -129,6 +129,7 @@ DAADCC4B19FB000C00987B1D /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; }; DAADCC6919FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m in Sources */ = {isa = PBXBuildFile; fileRef = DAADCC6719FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m */; }; DAADCC6A19FB00B500987B1D /* libKCOrderedAccessorFix.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAADCC5019FB006500987B1D /* libKCOrderedAccessorFix.a */; }; + DAAF16651F5897EA0013B8AE /* mpw-cli-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DAAF16631F5897EA0013B8AE /* mpw-cli-util.c */; }; DAB7AE5A1F3D74E700C856B1 /* libjson-c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB7AE591F3D74E700C856B1 /* libjson-c.a */; }; DAB7AE5B1F3D750B00C856B1 /* libjson-c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB7AE591F3D74E700C856B1 /* libjson-c.a */; }; DAB7AE941F3D757B00C856B1 /* libjson-c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB7AE901F3D757B00C856B1 /* libjson-c.a */; }; @@ -974,6 +975,8 @@ DAADCC5019FB006500987B1D /* libKCOrderedAccessorFix.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libKCOrderedAccessorFix.a; sourceTree = BUILT_PRODUCTS_DIR; }; DAADCC6619FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObjectModel+KCOrderedAccessorFix.h"; sourceTree = ""; }; DAADCC6719FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObjectModel+KCOrderedAccessorFix.m"; sourceTree = ""; }; + DAAF16631F5897EA0013B8AE /* mpw-cli-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-cli-util.c"; sourceTree = ""; }; + DAAF16641F5897EA0013B8AE /* mpw-cli-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-cli-util.h"; sourceTree = ""; }; DAB7AE591F3D74E700C856B1 /* libjson-c.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libjson-c.a"; path = "External/libjson-c/libjson-c-osx/lib/libjson-c.a"; sourceTree = ""; }; DAB7AE7B1F3D757B00C856B1 /* arraylist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arraylist.h; sourceTree = ""; }; DAB7AE7C1F3D757B00C856B1 /* bits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bits.h; sourceTree = ""; }; @@ -1292,6 +1295,8 @@ DA456CA21F5307B700D54152 /* blf.h */, DA4571181F552C9500D54152 /* blowfish.h */, DA1C7AB81F1A8F6E009A3551 /* mpw-bench.c */, + DAAF16631F5897EA0013B8AE /* mpw-cli-util.c */, + DAAF16641F5897EA0013B8AE /* mpw-cli-util.h */, DA1C7AB91F1A8F6E009A3551 /* mpw-cli.c */, DA1C7ABA1F1A8F6E009A3551 /* mpw-tests-util.c */, DA1C7ABB1F1A8F6E009A3551 /* mpw-tests-util.h */, @@ -2734,6 +2739,7 @@ 93D393A1646430FAAC73E7FE /* MPMacApplication.m in Sources */, DA7471A61F2B71B9005F3468 /* mpw-marshall-util.c in Sources */, 93D398D1F5D8CD5A22AF6929 /* MPGradientView.m in Sources */, + DAAF16651F5897EA0013B8AE /* mpw-cli-util.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/platform-independent/cli-c/build b/platform-independent/cli-c/build index 3ecdb4d4..f7bb4064 100755 --- a/platform-independent/cli-c/build +++ b/platform-independent/cli-c/build @@ -73,7 +73,7 @@ mpw() { ) # build - cc "${cflags[@]}" "$@" "core/base64.c" "core/mpw-algorithm.c" "core/mpw-types.c" "core/mpw-util.c" "core/mpw-marshall-util.c" "core/mpw-marshall.c" \ + cc "${cflags[@]}" "$@" "core/base64.c" "core/mpw-algorithm.c" "core/mpw-types.c" "core/mpw-util.c" "core/mpw-marshall-util.c" "core/mpw-marshall.c" "cli/mpw-cli-util.c" \ "${ldflags[@]}" "cli/mpw-cli.c" -o "mpw" echo "done! You can now run ./mpw-cli-tests, ./install or use ./$_" } diff --git a/platform-independent/cli-c/cli/mpw-cli-util.c b/platform-independent/cli-c/cli/mpw-cli-util.c new file mode 100644 index 00000000..b67a7010 --- /dev/null +++ b/platform-independent/cli-c/cli/mpw-cli-util.c @@ -0,0 +1,244 @@ +//============================================================================== +// This file is part of Master Password. +// Copyright (c) 2011-2017, Maarten Billemont. +// +// Master Password is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Master Password is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You can find a copy of the GNU General Public License in the +// LICENSE file. Alternatively, see . +//============================================================================== + +#include "mpw-cli-util.h" + +#include +#include +#include +#include +#include +#include + +#include "mpw-util.h" + +/** Read the value of an environment variable. + * @return A newly allocated string or NULL if the variable doesn't exist. */ +const char *mpw_getenv(const char *variableName) { + + char *envBuf = getenv( variableName ); + return envBuf? strdup( envBuf ): NULL; +} + +/** Use the askpass program to prompt the user. + * @return A newly allocated string or NULL if askpass is not supported or an error occurred. */ +char *mpw_askpass(const char *prompt) { + + const char *askpass = mpw_getenv( MP_ENV_askpass ); + if (!askpass) + return NULL; + + int pipes[2]; + if (pipe( pipes ) == ERR) { + wrn( "Couldn't pipe: %s\n", strerror( errno ) ); + return NULL; + } + + pid_t pid = fork(); + if (pid == ERR) { + wrn( "Couldn't fork for askpass:\n %s: %s\n", askpass, strerror( errno ) ); + return NULL; + } + + if (!pid) { + // askpass fork + close( pipes[0] ); + if (dup2( pipes[1], STDOUT_FILENO ) == ERR) + ftl( "Couldn't connect pipe to process: %s\n", strerror( errno ) ); + + else if (execlp( askpass, askpass, prompt, NULL ) == ERR) + ftl( "Couldn't execute askpass:\n %s: %s\n", askpass, strerror( errno ) ); + + exit( EX_SOFTWARE ); + } + + close( pipes[1] ); + char *answer = mpw_read_fd( pipes[0] ); + close( pipes[0] ); + int status; + if (waitpid( pid, &status, 0 ) == ERR) { + wrn( "Couldn't wait for askpass: %s\n", strerror( errno ) ); + mpw_free_string( &answer ); + return NULL; + } + + if (WIFEXITED( status ) && WEXITSTATUS( status ) == EXIT_SUCCESS && answer && strlen( answer )) { + // Remove trailing newline. + if (answer[strlen( answer ) - 1] == '\n') + answer[strlen( answer ) - 1] = '\0'; + return answer; + } + + mpw_free_string( &answer ); + return NULL; +} + +/** Ask the user a question. + * @return A newly allocated string or NULL if an error occurred trying to read from the user. */ +const char *mpw_getline(const char *prompt) { + + // Get answer from askpass. + char *answer = mpw_askpass( prompt ); + if (answer) + return answer; + + // Get password from terminal. + fprintf( stderr, "%s ", prompt ); + + size_t bufSize = 0; + ssize_t lineSize = getline( &answer, &bufSize, stdin ); + if (lineSize <= 1) { + mpw_free_string( &answer ); + return NULL; + } + + // Remove trailing newline. + answer[lineSize - 1] = '\0'; + return answer; +} + +/** Ask the user for a password. + * @return A newly allocated string or NULL if an error occurred trying to read from the user. */ +const char *mpw_getpass(const char *prompt) { + + // Get password from askpass. + const char *password = mpw_askpass( prompt ); + if (password) + return password; + + // Get password from terminal. + char *answer = getpass( prompt ); + if (!answer) + return NULL; + + password = strdup( answer ); + bzero( answer, strlen( answer ) ); + return password; +} + +/** Get the absolute path to the mpw configuration file with the given prefix name and file extension. + * Resolves the file as located in the <.mpw.d> directory inside the user's home directory + * or current directory if it couldn't be resolved. + * @return A newly allocated string. */ +const char *mpw_path(const char *prefix, const char *extension) { + + // Resolve user's home directory. + char *homeDir = NULL; + if (!homeDir) + if ((homeDir = getenv( "HOME" ))) + homeDir = strdup( homeDir ); + if (!homeDir) + if ((homeDir = getenv( "USERPROFILE" ))) + homeDir = strdup( homeDir ); + if (!homeDir) { + const char *homeDrive = getenv( "HOMEDRIVE" ), *homePath = getenv( "HOMEPATH" ); + if (homeDrive && homePath) + homeDir = strdup( mpw_str( "%s%s", homeDrive, homePath ) ); + } + if (!homeDir) { + struct passwd *passwd = getpwuid( getuid() ); + if (passwd) + homeDir = strdup( passwd->pw_dir ); + } + if (!homeDir) + homeDir = getcwd( NULL, 0 ); + + // Compose filename. + char *path = strdup( mpw_str( "%s.%s", prefix, extension ) ); + + // This is a filename, remove all potential directory separators. + for (char *slash; (slash = strstr( path, "/" )); *slash = '_'); + + // Compose pathname. + if (homeDir) { + const char *homePath = mpw_str( "%s/.mpw.d/%s", homeDir, path ); + free( homeDir ); + free( path ); + + if (homePath) + path = strdup( homePath ); + } + + return path; +} + +/** mkdir all the directories up to the directory of the given file path. + * @return true if the file's path exists. */ +bool mpw_mkdirs(const char *filePath) { + + if (!filePath) + return false; + + // The path to mkdir is the filePath without the last path component. + char *pathEnd = strrchr( filePath, '/' ); + char *path = pathEnd? strndup( filePath, (size_t)(pathEnd - filePath) ): NULL; + if (!path) + return false; + + // Save the cwd and for absolute paths, start at the root. + char *cwd = getcwd( NULL, 0 ); + if (*filePath == '/') + chdir( "/" ); + + // Walk the path. + bool success = true; + for (char *dirName = strtok( path, "/" ); success && dirName; dirName = strtok( NULL, "/" )) { + if (!strlen( dirName )) + continue; + + success &= (mkdir( dirName, 0700 ) != ERR || errno == EEXIST) && chdir( dirName ) != ERR; + } + free( path ); + + if (chdir( cwd ) == ERR) + wrn( "Could not restore cwd:\n %s: %s\n", cwd, strerror( errno ) ); + free( cwd ); + + return success; +} + +/** Read until EOF from the given file descriptor. + * @return A newly allocated string or NULL the read buffer couldn't be allocated. */ +char *mpw_read_fd(int fd) { + + char *buf = NULL; + size_t blockSize = 4096, bufSize = 0, bufOffset = 0; + ssize_t readSize = 0; + while ((mpw_realloc( &buf, &bufSize, blockSize )) && + ((readSize = read( fd, buf + bufOffset, blockSize )) > 0)); + if (readSize == ERR) + dbg( "While reading: %s\n", strerror( errno ) ); + + return buf; +} + +/** Read the file contents of a given file. + * @return A newly allocated string or NULL the read buffer couldn't be allocated. */ +char *mpw_read_file(FILE *file) { + + if (!file) + return NULL; + + char *buf = NULL; + size_t blockSize = 4096, bufSize = 0, bufOffset = 0, readSize = 0; + while ((mpw_realloc( &buf, &bufSize, blockSize )) && + (bufOffset += (readSize = fread( buf + bufOffset, 1, blockSize, file ))) && + (readSize == blockSize)); + + return buf; +} diff --git a/platform-independent/cli-c/cli/mpw-cli-util.h b/platform-independent/cli-c/cli/mpw-cli-util.h new file mode 100644 index 00000000..0ee275fc --- /dev/null +++ b/platform-independent/cli-c/cli/mpw-cli-util.h @@ -0,0 +1,64 @@ +//============================================================================== +// This file is part of Master Password. +// Copyright (c) 2011-2017, Maarten Billemont. +// +// Master Password is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Master Password is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You can find a copy of the GNU General Public License in the +// LICENSE file. Alternatively, see . +//============================================================================== + +#include +#include +#include + +#ifndef MP_VERSION +#define MP_VERSION ? +#endif + +#define MP_ENV_fullName "MPW_FULLNAME" +#define MP_ENV_algorithm "MPW_ALGORITHM" +#define MP_ENV_format "MPW_FORMAT" +#define MP_ENV_askpass "MPW_ASKPASS" + +/** Read the value of an environment variable. + * @return A newly allocated string or NULL if the variable doesn't exist. */ +const char *mpw_getenv(const char *variableName); + +/** Use the askpass program to prompt the user. + * @return A newly allocated string or NULL if askpass is not supported or an error occurred. */ +char *mpw_askpass(const char *prompt); + +/** Ask the user a question. + * @return A newly allocated string or NULL if an error occurred trying to read from the user. */ +const char *mpw_getline(const char *prompt); + +/** Ask the user for a password. + * @return A newly allocated string or NULL if an error occurred trying to read from the user. */ +const char *mpw_getpass(const char *prompt); + +/** Get the absolute path to the mpw configuration file with the given prefix name and file extension. + * Resolves the file as located in the <.mpw.d> directory inside the user's home directory + * or current directory if it couldn't be resolved. + * @return A newly allocated string. */ +const char *mpw_path(const char *prefix, const char *extension); + +/** mkdir all the directories up to the directory of the given file path. + * @return true if the file's path exists. */ +bool mpw_mkdirs(const char *filePath); + +/** Read until EOF from the given file descriptor. + * @return A newly allocated string or NULL the read buffer couldn't be allocated. */ +char *mpw_read_fd(int fd); + +/** Read the file contents of a given file. + * @return A newly allocated string or NULL the read buffer couldn't be allocated. */ +char *mpw_read_file(FILE *file); diff --git a/platform-independent/cli-c/cli/mpw-cli.c b/platform-independent/cli-c/cli/mpw-cli.c index 5326102c..d419e3b2 100644 --- a/platform-independent/cli-c/cli/mpw-cli.c +++ b/platform-independent/cli-c/cli/mpw-cli.c @@ -1,30 +1,15 @@ #include #include -#include -#include #include #include #include #include -#if defined(READLINE) -#include -#elif defined(EDITLINE) -#include -#endif - +#include "mpw-cli-util.h" #include "mpw-algorithm.h" #include "mpw-util.h" #include "mpw-marshall.h" -#ifndef MP_VERSION -#define MP_VERSION ? -#endif -#define MP_ENV_fullName "MPW_FULLNAME" -#define MP_ENV_algorithm "MPW_ALGORITHM" -#define MP_ENV_format "MPW_FORMAT" -#define MP_ENV_askpass "MPW_ASKPASS" - /** Output the program's usage documentation. */ static void usage() { @@ -111,225 +96,6 @@ static void usage() { exit( 0 ); } -/** Read the value of an environment variable. - * @return A newly allocated string or NULL if the variable doesn't exist. */ -static const char *mpw_getenv(const char *variableName) { - - char *envBuf = getenv( variableName ); - return envBuf? strdup( envBuf ): NULL; -} - -char *mpw_read_fd(int fd); -char *mpw_read_file(FILE *file); - -/** Use the askpass program to prompt the user. - * @return A newly allocated string or NULL if askpass is not supported or an error occurred. */ -static char *mpw_askpass(const char *prompt) { - - const char *askpass = mpw_getenv( MP_ENV_askpass ); - if (!askpass) - return NULL; - - int pipes[2]; - if (pipe( pipes ) == ERR) { - wrn( "Couldn't pipe: %s\n", strerror( errno ) ); - return NULL; - } - - pid_t pid = fork(); - if (pid == ERR) { - wrn( "Couldn't fork for askpass:\n %s: %s\n", askpass, strerror( errno ) ); - return NULL; - } - - if (!pid) { - // askpass fork - close( pipes[0] ); - if (dup2( pipes[1], STDOUT_FILENO ) == ERR) - ftl( "Couldn't connect pipe to process: %s\n", strerror( errno ) ); - - else if (execlp( askpass, askpass, prompt, NULL ) == ERR) - ftl( "Couldn't execute askpass:\n %s: %s\n", askpass, strerror( errno ) ); - - exit( EX_SOFTWARE ); - } - - close( pipes[1] ); - char *answer = mpw_read_fd( pipes[0] ); - close( pipes[0] ); - int status; - if (waitpid( pid, &status, 0 ) == ERR) { - wrn( "Couldn't wait for askpass: %s\n", strerror( errno ) ); - mpw_free_string( &answer ); - return NULL; - } - - if (WIFEXITED( status ) && WEXITSTATUS( status ) == EXIT_SUCCESS && answer && strlen( answer )) { - // Remove trailing newline. - if (answer[strlen( answer ) - 1] == '\n') - answer[strlen( answer ) - 1] = '\0'; - return answer; - } - - mpw_free_string( &answer ); - return NULL; -} - -/** Ask the user a question. - * @return A newly allocated string or NULL if an error occurred trying to read from the user. */ -static const char *mpw_getline(const char *prompt) { - - // Get answer from askpass. - char *answer = mpw_askpass( prompt ); - if (answer) - return answer; - - // Get password from terminal. - fprintf( stderr, "%s ", prompt ); - - size_t bufSize = 0; - ssize_t lineSize = getline( &answer, &bufSize, stdin ); - if (lineSize <= 1) { - mpw_free_string( &answer ); - return NULL; - } - - // Remove trailing newline. - answer[lineSize - 1] = '\0'; - return answer; -} - -/** Ask the user for a password. - * @return A newly allocated string or NULL if an error occurred trying to read from the user. */ -static const char *mpw_getpass(const char *prompt) { - - // Get password from askpass. - const char *password = mpw_askpass( prompt ); - if (password) - return password; - - // Get password from terminal. - char *answer = getpass( prompt ); - if (!answer) - return NULL; - - password = strdup( answer ); - bzero( answer, strlen( answer ) ); - return password; -} - -/** Get the absolute path to the mpw configuration file with the given prefix name and file extension. - * Resolves the file as located in the <.mpw.d> directory inside the user's home directory - * or current directory if it couldn't be resolved. - * @return A newly allocated string. */ -static const char *mpw_path(const char *prefix, const char *extension) { - - // Resolve user's home directory. - char *homeDir = NULL; - if (!homeDir) - if ((homeDir = getenv( "HOME" ))) - homeDir = strdup( homeDir ); - if (!homeDir) - if ((homeDir = getenv( "USERPROFILE" ))) - homeDir = strdup( homeDir ); - if (!homeDir) { - const char *homeDrive = getenv( "HOMEDRIVE" ), *homePath = getenv( "HOMEPATH" ); - if (homeDrive && homePath) - homeDir = strdup( mpw_str( "%s%s", homeDrive, homePath ) ); - } - if (!homeDir) { - struct passwd *passwd = getpwuid( getuid() ); - if (passwd) - homeDir = strdup( passwd->pw_dir ); - } - if (!homeDir) - homeDir = getcwd( NULL, 0 ); - - // Compose filename. - char *path = strdup( mpw_str( "%s.%s", prefix, extension ) ); - - // This is a filename, remove all potential directory separators. - for (char *slash; (slash = strstr( path, "/" )); *slash = '_'); - - // Compose pathname. - if (homeDir) { - const char *homePath = mpw_str( "%s/.mpw.d/%s", homeDir, path ); - free( homeDir ); - free( path ); - - if (homePath) - path = strdup( homePath ); - } - - return path; -} - -/** mkdir all the directories up to the directory of the given file path. - * @return true if the file's path exists. */ -static bool mpw_mkdirs(const char *filePath) { - - if (!filePath) - return false; - - // The path to mkdir is the filePath without the last path component. - char *pathEnd = strrchr( filePath, '/' ); - char *path = pathEnd? strndup( filePath, (size_t)(pathEnd - filePath) ): NULL; - if (!path) - return false; - - // Save the cwd and for absolute paths, start at the root. - char *cwd = getcwd( NULL, 0 ); - if (*filePath == '/') - chdir( "/" ); - - // Walk the path. - bool success = true; - for (char *dirName = strtok( path, "/" ); success && dirName; dirName = strtok( NULL, "/" )) { - if (!strlen( dirName )) - continue; - - success &= (mkdir( dirName, 0700 ) != ERR || errno == EEXIST) && chdir( dirName ) != ERR; - } - free( path ); - - if (chdir( cwd ) == ERR) - wrn( "Could not restore cwd:\n %s: %s\n", cwd, strerror( errno ) ); - free( cwd ); - - return success; -} - -/** Read until EOF from the given file descriptor. - * @return A newly allocated string or NULL the read buffer couldn't be allocated. */ -char *mpw_read_fd(int fd) { - - char *buf = NULL; - size_t blockSize = 4096, bufSize = 0, bufOffset = 0; - ssize_t readSize = 0; - while ((mpw_realloc( &buf, &bufSize, blockSize )) && - ((readSize = read( fd, buf + bufOffset, blockSize )) > 0)); - if (readSize == ERR) - dbg( "While reading: %s\n", strerror( errno ) ); - - return buf; -} - -/** Read the file contents of a given file. - * @return A newly allocated string or NULL the read buffer couldn't be allocated. */ -char *mpw_read_file(FILE *file) { - - if (!file) - return NULL; - - char *buf = NULL; - size_t blockSize = 4096, bufSize = 0, bufOffset = 0, readSize = 0; - while ((mpw_realloc( &buf, &bufSize, blockSize )) && - (bufOffset += (readSize = fread( buf + bufOffset, 1, blockSize, file ))) && - (readSize == blockSize)); - - return buf; -} - /** ======================================================================== * MAIN */ int main(const int argc, char *const argv[]) {