2
0

Extract cli utilities into mpw-cli-util to keep things cleaner.

This commit is contained in:
Maarten Billemont 2017-08-31 15:30:42 -04:00
parent 92a3a0ccbd
commit 568401a612
6 changed files with 325 additions and 242 deletions

View File

@ -31,42 +31,42 @@ extern int mpw_verbosity;
#define trc_level 3 #define trc_level 3
/** Logging internal state. */ /** Logging internal state. */
#define trc(...) ({ \ #define trc(...) ({ \
if (mpw_verbosity >= 3) \ if (mpw_verbosity >= trc_level) \
fprintf( stderr, __VA_ARGS__ ); }) fprintf( stderr, __VA_ARGS__ ); })
#endif #endif
#ifndef dbg #ifndef dbg
#define dbg_level 2 #define dbg_level 2
/** Logging state and events interesting when investigating issues. */ /** Logging state and events interesting when investigating issues. */
#define dbg(...) ({ \ #define dbg(...) ({ \
if (mpw_verbosity >= 2) \ if (mpw_verbosity >= dbg_level) \
fprintf( stderr, __VA_ARGS__ ); }) fprintf( stderr, __VA_ARGS__ ); })
#endif #endif
#ifndef inf #ifndef inf
#define inf_level 1 #define inf_level 1
/** User messages. */ /** User messages. */
#define inf(...) ({ \ #define inf(...) ({ \
if (mpw_verbosity >= 1) \ if (mpw_verbosity >= inf_level) \
fprintf( stderr, __VA_ARGS__ ); }) fprintf( stderr, __VA_ARGS__ ); })
#endif #endif
#ifndef wrn #ifndef wrn
#define wrn_level 0 #define wrn_level 0
/** Recoverable issues and user suggestions. */ /** Recoverable issues and user suggestions. */
#define wrn(...) ({ \ #define wrn(...) ({ \
if (mpw_verbosity >= 0) \ if (mpw_verbosity >= wrn_level) \
fprintf( stderr, __VA_ARGS__ ); }) fprintf( stderr, __VA_ARGS__ ); })
#endif #endif
#ifndef err #ifndef err
#define err_level -1 #define err_level -1
/** Unrecoverable issues. */ /** Unrecoverable issues. */
#define err(...) ({ \ #define err(...) ({ \
if (mpw_verbosity >= -1) \ if (mpw_verbosity >= err_level) \
fprintf( stderr, __VA_ARGS__ ); }) fprintf( stderr, __VA_ARGS__ ); })
#endif #endif
#ifndef ftl #ifndef ftl
#define ftl_level -2 #define ftl_level -2
/** Issues that lead to abortion. */ /** Issues that lead to abortion. */
#define ftl(...) ({ \ #define ftl(...) ({ \
if (mpw_verbosity >= -2) \ if (mpw_verbosity >= ftl_level) \
fprintf( stderr, __VA_ARGS__ ); }) fprintf( stderr, __VA_ARGS__ ); })
#endif #endif
@ -85,6 +85,9 @@ extern int mpw_verbosity;
#ifndef ERR #ifndef ERR
#define ERR -1 #define ERR -1
#endif #endif
#ifndef OK
#define OK 0
#endif
#ifndef stringify #ifndef stringify
#define stringify(s) #s #define stringify(s) #s
#endif #endif

View File

@ -129,6 +129,7 @@
DAADCC4B19FB000C00987B1D /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; }; DAADCC4B19FB000C00987B1D /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; };
DAADCC6919FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m in Sources */ = {isa = PBXBuildFile; fileRef = DAADCC6719FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m */; }; DAADCC6919FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m in Sources */ = {isa = PBXBuildFile; fileRef = DAADCC6719FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m */; };
DAADCC6A19FB00B500987B1D /* libKCOrderedAccessorFix.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAADCC5019FB006500987B1D /* libKCOrderedAccessorFix.a */; }; 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 */; }; 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 */; }; 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 */; }; 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; }; 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 = "<group>"; }; DAADCC6619FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObjectModel+KCOrderedAccessorFix.h"; sourceTree = "<group>"; };
DAADCC6719FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObjectModel+KCOrderedAccessorFix.m"; sourceTree = "<group>"; }; DAADCC6719FB007F00987B1D /* NSManagedObjectModel+KCOrderedAccessorFix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObjectModel+KCOrderedAccessorFix.m"; sourceTree = "<group>"; };
DAAF16631F5897EA0013B8AE /* mpw-cli-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-cli-util.c"; sourceTree = "<group>"; };
DAAF16641F5897EA0013B8AE /* mpw-cli-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-cli-util.h"; sourceTree = "<group>"; };
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 = "<group>"; }; 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 = "<group>"; };
DAB7AE7B1F3D757B00C856B1 /* arraylist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arraylist.h; sourceTree = "<group>"; }; DAB7AE7B1F3D757B00C856B1 /* arraylist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arraylist.h; sourceTree = "<group>"; };
DAB7AE7C1F3D757B00C856B1 /* bits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bits.h; sourceTree = "<group>"; }; DAB7AE7C1F3D757B00C856B1 /* bits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bits.h; sourceTree = "<group>"; };
@ -1292,6 +1295,8 @@
DA456CA21F5307B700D54152 /* blf.h */, DA456CA21F5307B700D54152 /* blf.h */,
DA4571181F552C9500D54152 /* blowfish.h */, DA4571181F552C9500D54152 /* blowfish.h */,
DA1C7AB81F1A8F6E009A3551 /* mpw-bench.c */, DA1C7AB81F1A8F6E009A3551 /* mpw-bench.c */,
DAAF16631F5897EA0013B8AE /* mpw-cli-util.c */,
DAAF16641F5897EA0013B8AE /* mpw-cli-util.h */,
DA1C7AB91F1A8F6E009A3551 /* mpw-cli.c */, DA1C7AB91F1A8F6E009A3551 /* mpw-cli.c */,
DA1C7ABA1F1A8F6E009A3551 /* mpw-tests-util.c */, DA1C7ABA1F1A8F6E009A3551 /* mpw-tests-util.c */,
DA1C7ABB1F1A8F6E009A3551 /* mpw-tests-util.h */, DA1C7ABB1F1A8F6E009A3551 /* mpw-tests-util.h */,
@ -2734,6 +2739,7 @@
93D393A1646430FAAC73E7FE /* MPMacApplication.m in Sources */, 93D393A1646430FAAC73E7FE /* MPMacApplication.m in Sources */,
DA7471A61F2B71B9005F3468 /* mpw-marshall-util.c in Sources */, DA7471A61F2B71B9005F3468 /* mpw-marshall-util.c in Sources */,
93D398D1F5D8CD5A22AF6929 /* MPGradientView.m in Sources */, 93D398D1F5D8CD5A22AF6929 /* MPGradientView.m in Sources */,
DAAF16651F5897EA0013B8AE /* mpw-cli-util.c in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@ -73,7 +73,7 @@ mpw() {
) )
# build # 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" "${ldflags[@]}" "cli/mpw-cli.c" -o "mpw"
echo "done! You can now run ./mpw-cli-tests, ./install or use ./$_" echo "done! You can now run ./mpw-cli-tests, ./install or use ./$_"
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
//==============================================================================
#include "mpw-cli-util.h"
#include <unistd.h>
#include <sys/stat.h>
#include <pwd.h>
#include <string.h>
#include <errno.h>
#include <sysexits.h>
#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 <prefix.extension> 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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
//==============================================================================
#include <stdio.h>
#include <stdint.h>
#include <stdbool.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"
/** 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 <prefix.extension> 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);

View File

@ -1,30 +1,15 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h>
#include <pwd.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <sysexits.h> #include <sysexits.h>
#if defined(READLINE) #include "mpw-cli-util.h"
#include <readline/readline.h>
#elif defined(EDITLINE)
#include <histedit.h>
#endif
#include "mpw-algorithm.h" #include "mpw-algorithm.h"
#include "mpw-util.h" #include "mpw-util.h"
#include "mpw-marshall.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. */ /** Output the program's usage documentation. */
static void usage() { static void usage() {
@ -111,225 +96,6 @@ static void usage() {
exit( 0 ); 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 <prefix.extension> 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 */ * MAIN */
int main(const int argc, char *const argv[]) { int main(const int argc, char *const argv[]) {