Skip to content

Commit

Permalink
Merge branch 'cygpath'
Browse files Browse the repository at this point in the history
This topic branch adds the `cygpath` applet that closely imitates
MSYS2's tool of the same name.

Further, the `PATH` variable is special-cased to get the `cygpath`
treatment automagically: whenever a user sets it to a Unix-style path
list, we now try to convert that automatically to a Windows-style path
list (albeit with forward-slashes).

Signed-off-by: Johannes Schindelin <[email protected]>
  • Loading branch information
dscho committed Sep 13, 2022
2 parents 72751b4 + 1eb44d0 commit 985b51c
Show file tree
Hide file tree
Showing 11 changed files with 381 additions and 39 deletions.
1 change: 1 addition & 0 deletions configs/mingw32_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ CONFIG_FEATURE_EURO=y
CONFIG_SKIP_ANSI_EMULATION_DEFAULT=2
CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y
CONFIG_FEATURE_EXTRA_FILE_DATA=y
CONFIG_CYGPATH=y

#
# Build Options
Expand Down
1 change: 1 addition & 0 deletions configs/mingw64_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ CONFIG_FEATURE_EURO=y
CONFIG_SKIP_ANSI_EMULATION_DEFAULT=2
CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y
CONFIG_FEATURE_EXTRA_FILE_DATA=y
CONFIG_CYGPATH=y

#
# Build Options
Expand Down
51 changes: 39 additions & 12 deletions libbb/appletlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
static inline int *get_perrno(void) { return &errno; }

#include "busybox.h"
#include "path-convert.h"

#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
|| defined(__APPLE__) \
Expand Down Expand Up @@ -1225,6 +1226,22 @@ get_script_content(unsigned n UNUSED_PARAM)

#endif /* defined(SINGLE_APPLET_MAIN) */

#if ENABLE_PLATFORM_MINGW32
static char *xwcstoutf(wchar_t *wcs)
{
DWORD size = WideCharToMultiByte(CP_UTF8, 0, wcs, -1, NULL, 0, NULL, NULL) + 1;
char *buf;

if (!size)
bb_error_msg_and_die("could not convert '%ls' to UTF-8", wcs);
buf = xmalloc(size);
if (WideCharToMultiByte(CP_UTF8, 0, wcs, -1, buf, size, NULL, NULL))
return buf;
free(buf);
bb_error_msg_and_die("could not convert '%ls' to UTF-8", wcs);
}
#endif

#if ENABLE_BUILD_LIBBUSYBOX
int lbb_main(char **argv)
#else
Expand Down Expand Up @@ -1333,19 +1350,29 @@ int main(int argc UNUSED_PARAM, char **argv)
/* Manually convert non-ASCII environment entries from UTF-16 */
for (i = 0; wenv[i]; i++) {
for (p = wenv + i; *p; p++)
if (*p & ~0x7f) {
if (!_wcsnicmp(wenv + i, L"PATH=", 5)) {
char *orig = xwcstoutf(wenv + i + 5);
char *converted = path_convert_path_list(orig, PATH_CONVERT_MIXED);
size_t len;

if (!converted)
bb_error_msg_and_die("could not convert path list '%s'", orig);

len = strlen(converted);
converted = xrealloc(converted, len + 6);
memmove(converted + 5, converted, len + 1);
memcpy(converted, "PATH=", 5);

putenv(converted);

free(converted);
free(orig);
break;
} else if (*p & ~0x7f) {
/* Non-ASCII name or value */
DWORD size = WideCharToMultiByte(CP_UTF8, 0,
wenv + i, -1, NULL, 0, NULL, NULL) + 1;
char *buf;

if (!size)
break;
buf = malloc(size);
if (!buf)
bb_error_msg_and_die("Out of memory");
if (WideCharToMultiByte(CP_UTF8, 0,
wenv + i, -1, buf, size, NULL, NULL))
char *buf = xwcstoutf(wenv + i);

if (buf)
/* if we could not convert, punt */
putenv(buf);
free(buf);
Expand Down
86 changes: 86 additions & 0 deletions miscutils/cygpath.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Convert MSYS-style paths to Win32-style ones
*
* Copyright (C) 2022 Johannes Schindelin <[email protected]>
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
//config:config CYGPATH
//config: bool "cygpath"
//config: default y
//config: depends on PLATFORM_MINGW32
//config: help
//config: Convert Unix and Windows format paths

//applet:IF_CYGPATH(APPLET_NOEXEC(cygpath, cygpath, BB_DIR_USR_BIN, BB_SUID_DROP, cygpath))

//kbuild:lib-$(CONFIG_CYGPATH) += cygpath.o

//usage:#define cygpath_trivial_usage
//usage: "ARG|ARGS"
//usage:#define cygpath_full_usage "\n\n"
//usage: "Convert Unix and Windows format paths"

#include "libbb.h"
#include "path-convert.h"

enum {
OPT_absolute = (1 << 0),
OPT_mixed = (1 << 1),
OPT_unix = (1 << 2),
OPT_windows = (1 << 3),
OPT_path_list = (1 << 4),
};

int cygpath_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int cygpath_main(int argc UNUSED_PARAM, char **argv)
{
int i;
enum path_convert_flag flags = 0;
#if ENABLE_LONG_OPTS
static const char cygpath_longopts[] ALIGN1 =
"absolute\0" No_argument "a"
"mixed\0" No_argument "m"
"unix\0" No_argument "u"
"windows\0" No_argument "w"
"path\0" No_argument "p"
;
#endif
int opt = getopt32long(argv, "amuwp", cygpath_longopts);
argv += optind;
argc -= optind;

if (opt & OPT_absolute)
flags |= PATH_CONVERT_ABSOLUTE;
if (opt & OPT_mixed)
flags |= PATH_CONVERT_MIXED;
if (opt & OPT_unix)
flags |= PATH_CONVERT_UNIX;
if (opt & OPT_windows)
flags |= PATH_CONVERT_WINDOWS;

for (i = 0; i < argc; i++) {
char *to_free = NULL;
const char *path = argv[i], *result;
char buffer[PATH_MAX_LONG];

if (!*argv[i]) {
bb_error_msg("can't convert empty path");
return EXIT_FAILURE;
}

if (opt & OPT_path_list)
result = to_free = path_convert_path_list(path, flags);
else
result = path_convert(path, buffer, sizeof(buffer), flags);

if (!result)
return EXIT_FAILURE;

printf("%s\n", result);

free(to_free);
}

return EXIT_SUCCESS;
}
39 changes: 14 additions & 25 deletions shell/ash.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ typedef long arith_t;
# define CLEAR_RANDOM_T(rnd) ((void)0)
#endif

#if ENABLE_PLATFORM_MINGW32
# include "path-convert.h"
#endif

#include "NUM_APPLETS.h"
#if NUM_APPLETS == 1
/* STANDALONE does not make sense, and won't compile */
Expand Down Expand Up @@ -2624,34 +2628,19 @@ bltinlookup(const char *name)
static char *
fix_pathvar(const char *path, int len)
{
char *newpath = xstrdup(path);
char *p;
int modified = FALSE;

p = newpath + len;
while (*p) {
if (*p != ':' && *p != ';') {
/* skip drive */
if (isalpha(*p) && p[1] == ':')
p += 2;
/* skip through path component */
for (; *p != '\0' && *p != ':' && *p != ';'; ++p)
continue;
}
/* *p is ':', ';' or '\0' here */
if (*p == ':') {
*p++ = ';';
modified = TRUE;
}
else if (*p == ';') {
++p;
}
}
char *newpath = path_convert_path_list(path + len, PATH_CONVERT_MIXED);
size_t len2;

if (!modified) {
if (!strcmp(path + len, newpath)) {
free(newpath);
newpath = NULL;
return NULL;
}

len2 = strlen(newpath);
newpath = xrealloc(newpath, len + len2 + 1);
memmove(newpath + len, newpath, len2 + 1);
memcpy(newpath, path, len);

return newpath;
}

Expand Down
54 changes: 54 additions & 0 deletions testsuite/cygpath.tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/bin/sh
set -x

# Copyright 2022 by Johannes Schindelin <[email protected]>
# Licensed under GPLv2, see file LICENSE in this source tree.

. ./testing.sh

# testing "description" "command" "result" "infile" "stdin"
pseudo_root="$(cygpath -am /)"
res=$?
case "$pseudo_root" in */) s="ends in a slash";; *) s=;; esac

testing "pseudo root" 'echo $res,$s,"$pseudo_root"' "0,,$pseudo_root
" "" ""

testing "cygpath -aw" "cygpath -aw /c/ /c/123 /c/324" 'C:\\
C:\\123
C:\\324
' "" ""

testing "cygpath -am" "cygpath -am /c/ /c/123 /c/324 / /lib" "C:/
C:/123
C:/324
$pseudo_root
${pseudo_root}/lib
" "" ""

testing "cygpath -u" 'cygpath -u C:\\ abc' '/c/
abc
' "" ""

testing "cygpath and dots" 'cygpath -am . ./. ../. ./..' "$PWD
$PWD
${PWD%/*}
${PWD%/*}
" "" ""

testing "cygpath ''" '! cygpath "" abc 2>&1' "cygpath: can't convert empty path
" "" ""

testing "cygpath -p" 'cygpath -pam .:./.:/c/' "$PWD;$PWD;C:/
" "" ""

testing "cygpath -p" 'cygpath -pam .:' "$PWD;$PWD
" "" ""

testing "cygpath -u" "cygpath -pau \"$pseudo_root;${pseudo_root}/lib\"" "/:/lib
" "" ""

testing "cygpath -m <unix-style-path>" "cygpath -m /lib" "$pseudo_root/lib
" "" ""

exit $FAILCOUNT
1 change: 1 addition & 0 deletions win32/Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ lib-$(CONFIG_PLATFORM_MINGW32) += process.o
lib-$(CONFIG_PLATFORM_MINGW32) += match_class.o
lib-$(CONFIG_PLATFORM_MINGW32) += mntent.o
lib-$(CONFIG_PLATFORM_MINGW32) += net.o
lib-$(CONFIG_PLATFORM_MINGW32) += path-convert.o
lib-$(CONFIG_PLATFORM_MINGW32) += poll.o
lib-$(CONFIG_PLATFORM_MINGW32) += popen.o
lib-$(CONFIG_PLATFORM_MINGW32) += regex.o
Expand Down
7 changes: 6 additions & 1 deletion win32/env.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "libbb.h"
#include "path-convert.h"

#undef getenv
#undef putenv
Expand All @@ -18,16 +19,20 @@ char *mingw_getenv(const char *name)
int setenv(const char *name, const char *value, int replace)
{
int out;
char *envstr;
char *envstr, *to_free = NULL;

if (!name || !*name || strchr(name, '=') || !value) return -1;
if (!replace) {
if (getenv(name)) return 0;
}

if (!strcmp(name, "PATH"))
value = to_free = path_convert_path_list(value, PATH_CONVERT_WINDOWS);

envstr = xasprintf("%s=%s", name, value);
out = mingw_putenv(envstr);
free(envstr);
free(to_free);

return out;
}
Expand Down
2 changes: 1 addition & 1 deletion win32/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ wchar_t *mingw_pathconv(const char *path)
pseudo_root[pseudo_root_len++] = L'\\';
}

memcpy(result, pseudo_root, pseudo_root_len * sizeof(wchar_t));
memcpy(result, pseudo_root, (pseudo_root_len - !path[1]) * sizeof(wchar_t));
return pathconv_rest(result, pseudo_root_len, path + 1);
}

Expand Down
Loading

0 comments on commit 985b51c

Please sign in to comment.