Skip to content

Commit 985b51c

Browse files
committed
Merge branch 'cygpath'
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 <johannes.schindelin@gmx.de>
2 parents 72751b4 + 1eb44d0 commit 985b51c

File tree

11 files changed

+381
-39
lines changed

11 files changed

+381
-39
lines changed

configs/mingw32_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ CONFIG_FEATURE_EURO=y
5555
CONFIG_SKIP_ANSI_EMULATION_DEFAULT=2
5656
CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y
5757
CONFIG_FEATURE_EXTRA_FILE_DATA=y
58+
CONFIG_CYGPATH=y
5859

5960
#
6061
# Build Options

configs/mingw64_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ CONFIG_FEATURE_EURO=y
5555
CONFIG_SKIP_ANSI_EMULATION_DEFAULT=2
5656
CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y
5757
CONFIG_FEATURE_EXTRA_FILE_DATA=y
58+
CONFIG_CYGPATH=y
5859

5960
#
6061
# Build Options

libbb/appletlib.c

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
static inline int *get_perrno(void) { return &errno; }
3232

3333
#include "busybox.h"
34+
#include "path-convert.h"
3435

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

12261227
#endif /* defined(SINGLE_APPLET_MAIN) */
12271228

1229+
#if ENABLE_PLATFORM_MINGW32
1230+
static char *xwcstoutf(wchar_t *wcs)
1231+
{
1232+
DWORD size = WideCharToMultiByte(CP_UTF8, 0, wcs, -1, NULL, 0, NULL, NULL) + 1;
1233+
char *buf;
1234+
1235+
if (!size)
1236+
bb_error_msg_and_die("could not convert '%ls' to UTF-8", wcs);
1237+
buf = xmalloc(size);
1238+
if (WideCharToMultiByte(CP_UTF8, 0, wcs, -1, buf, size, NULL, NULL))
1239+
return buf;
1240+
free(buf);
1241+
bb_error_msg_and_die("could not convert '%ls' to UTF-8", wcs);
1242+
}
1243+
#endif
1244+
12281245
#if ENABLE_BUILD_LIBBUSYBOX
12291246
int lbb_main(char **argv)
12301247
#else
@@ -1333,19 +1350,29 @@ int main(int argc UNUSED_PARAM, char **argv)
13331350
/* Manually convert non-ASCII environment entries from UTF-16 */
13341351
for (i = 0; wenv[i]; i++) {
13351352
for (p = wenv + i; *p; p++)
1336-
if (*p & ~0x7f) {
1353+
if (!_wcsnicmp(wenv + i, L"PATH=", 5)) {
1354+
char *orig = xwcstoutf(wenv + i + 5);
1355+
char *converted = path_convert_path_list(orig, PATH_CONVERT_MIXED);
1356+
size_t len;
1357+
1358+
if (!converted)
1359+
bb_error_msg_and_die("could not convert path list '%s'", orig);
1360+
1361+
len = strlen(converted);
1362+
converted = xrealloc(converted, len + 6);
1363+
memmove(converted + 5, converted, len + 1);
1364+
memcpy(converted, "PATH=", 5);
1365+
1366+
putenv(converted);
1367+
1368+
free(converted);
1369+
free(orig);
1370+
break;
1371+
} else if (*p & ~0x7f) {
13371372
/* Non-ASCII name or value */
1338-
DWORD size = WideCharToMultiByte(CP_UTF8, 0,
1339-
wenv + i, -1, NULL, 0, NULL, NULL) + 1;
1340-
char *buf;
1341-
1342-
if (!size)
1343-
break;
1344-
buf = malloc(size);
1345-
if (!buf)
1346-
bb_error_msg_and_die("Out of memory");
1347-
if (WideCharToMultiByte(CP_UTF8, 0,
1348-
wenv + i, -1, buf, size, NULL, NULL))
1373+
char *buf = xwcstoutf(wenv + i);
1374+
1375+
if (buf)
13491376
/* if we could not convert, punt */
13501377
putenv(buf);
13511378
free(buf);

miscutils/cygpath.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Convert MSYS-style paths to Win32-style ones
3+
*
4+
* Copyright (C) 2022 Johannes Schindelin <johannes.schindelin@gmx.de>
5+
*
6+
* Licensed under GPLv2, see file LICENSE in this source tree.
7+
*/
8+
//config:config CYGPATH
9+
//config: bool "cygpath"
10+
//config: default y
11+
//config: depends on PLATFORM_MINGW32
12+
//config: help
13+
//config: Convert Unix and Windows format paths
14+
15+
//applet:IF_CYGPATH(APPLET_NOEXEC(cygpath, cygpath, BB_DIR_USR_BIN, BB_SUID_DROP, cygpath))
16+
17+
//kbuild:lib-$(CONFIG_CYGPATH) += cygpath.o
18+
19+
//usage:#define cygpath_trivial_usage
20+
//usage: "ARG|ARGS"
21+
//usage:#define cygpath_full_usage "\n\n"
22+
//usage: "Convert Unix and Windows format paths"
23+
24+
#include "libbb.h"
25+
#include "path-convert.h"
26+
27+
enum {
28+
OPT_absolute = (1 << 0),
29+
OPT_mixed = (1 << 1),
30+
OPT_unix = (1 << 2),
31+
OPT_windows = (1 << 3),
32+
OPT_path_list = (1 << 4),
33+
};
34+
35+
int cygpath_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
36+
int cygpath_main(int argc UNUSED_PARAM, char **argv)
37+
{
38+
int i;
39+
enum path_convert_flag flags = 0;
40+
#if ENABLE_LONG_OPTS
41+
static const char cygpath_longopts[] ALIGN1 =
42+
"absolute\0" No_argument "a"
43+
"mixed\0" No_argument "m"
44+
"unix\0" No_argument "u"
45+
"windows\0" No_argument "w"
46+
"path\0" No_argument "p"
47+
;
48+
#endif
49+
int opt = getopt32long(argv, "amuwp", cygpath_longopts);
50+
argv += optind;
51+
argc -= optind;
52+
53+
if (opt & OPT_absolute)
54+
flags |= PATH_CONVERT_ABSOLUTE;
55+
if (opt & OPT_mixed)
56+
flags |= PATH_CONVERT_MIXED;
57+
if (opt & OPT_unix)
58+
flags |= PATH_CONVERT_UNIX;
59+
if (opt & OPT_windows)
60+
flags |= PATH_CONVERT_WINDOWS;
61+
62+
for (i = 0; i < argc; i++) {
63+
char *to_free = NULL;
64+
const char *path = argv[i], *result;
65+
char buffer[PATH_MAX_LONG];
66+
67+
if (!*argv[i]) {
68+
bb_error_msg("can't convert empty path");
69+
return EXIT_FAILURE;
70+
}
71+
72+
if (opt & OPT_path_list)
73+
result = to_free = path_convert_path_list(path, flags);
74+
else
75+
result = path_convert(path, buffer, sizeof(buffer), flags);
76+
77+
if (!result)
78+
return EXIT_FAILURE;
79+
80+
printf("%s\n", result);
81+
82+
free(to_free);
83+
}
84+
85+
return EXIT_SUCCESS;
86+
}

shell/ash.c

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,10 @@ typedef long arith_t;
317317
# define CLEAR_RANDOM_T(rnd) ((void)0)
318318
#endif
319319

320+
#if ENABLE_PLATFORM_MINGW32
321+
# include "path-convert.h"
322+
#endif
323+
320324
#include "NUM_APPLETS.h"
321325
#if NUM_APPLETS == 1
322326
/* STANDALONE does not make sense, and won't compile */
@@ -2624,34 +2628,19 @@ bltinlookup(const char *name)
26242628
static char *
26252629
fix_pathvar(const char *path, int len)
26262630
{
2627-
char *newpath = xstrdup(path);
2628-
char *p;
2629-
int modified = FALSE;
2630-
2631-
p = newpath + len;
2632-
while (*p) {
2633-
if (*p != ':' && *p != ';') {
2634-
/* skip drive */
2635-
if (isalpha(*p) && p[1] == ':')
2636-
p += 2;
2637-
/* skip through path component */
2638-
for (; *p != '\0' && *p != ':' && *p != ';'; ++p)
2639-
continue;
2640-
}
2641-
/* *p is ':', ';' or '\0' here */
2642-
if (*p == ':') {
2643-
*p++ = ';';
2644-
modified = TRUE;
2645-
}
2646-
else if (*p == ';') {
2647-
++p;
2648-
}
2649-
}
2631+
char *newpath = path_convert_path_list(path + len, PATH_CONVERT_MIXED);
2632+
size_t len2;
26502633

2651-
if (!modified) {
2634+
if (!strcmp(path + len, newpath)) {
26522635
free(newpath);
2653-
newpath = NULL;
2636+
return NULL;
26542637
}
2638+
2639+
len2 = strlen(newpath);
2640+
newpath = xrealloc(newpath, len + len2 + 1);
2641+
memmove(newpath + len, newpath, len2 + 1);
2642+
memcpy(newpath, path, len);
2643+
26552644
return newpath;
26562645
}
26572646

testsuite/cygpath.tests

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/bin/sh
2+
set -x
3+
4+
# Copyright 2022 by Johannes Schindelin <johannes.schindelin@gmx.de>
5+
# Licensed under GPLv2, see file LICENSE in this source tree.
6+
7+
. ./testing.sh
8+
9+
# testing "description" "command" "result" "infile" "stdin"
10+
pseudo_root="$(cygpath -am /)"
11+
res=$?
12+
case "$pseudo_root" in */) s="ends in a slash";; *) s=;; esac
13+
14+
testing "pseudo root" 'echo $res,$s,"$pseudo_root"' "0,,$pseudo_root
15+
" "" ""
16+
17+
testing "cygpath -aw" "cygpath -aw /c/ /c/123 /c/324" 'C:\\
18+
C:\\123
19+
C:\\324
20+
' "" ""
21+
22+
testing "cygpath -am" "cygpath -am /c/ /c/123 /c/324 / /lib" "C:/
23+
C:/123
24+
C:/324
25+
$pseudo_root
26+
${pseudo_root}/lib
27+
" "" ""
28+
29+
testing "cygpath -u" 'cygpath -u C:\\ abc' '/c/
30+
abc
31+
' "" ""
32+
33+
testing "cygpath and dots" 'cygpath -am . ./. ../. ./..' "$PWD
34+
$PWD
35+
${PWD%/*}
36+
${PWD%/*}
37+
" "" ""
38+
39+
testing "cygpath ''" '! cygpath "" abc 2>&1' "cygpath: can't convert empty path
40+
" "" ""
41+
42+
testing "cygpath -p" 'cygpath -pam .:./.:/c/' "$PWD;$PWD;C:/
43+
" "" ""
44+
45+
testing "cygpath -p" 'cygpath -pam .:' "$PWD;$PWD
46+
" "" ""
47+
48+
testing "cygpath -u" "cygpath -pau \"$pseudo_root;${pseudo_root}/lib\"" "/:/lib
49+
" "" ""
50+
51+
testing "cygpath -m <unix-style-path>" "cygpath -m /lib" "$pseudo_root/lib
52+
" "" ""
53+
54+
exit $FAILCOUNT

win32/Kbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ lib-$(CONFIG_PLATFORM_MINGW32) += process.o
1616
lib-$(CONFIG_PLATFORM_MINGW32) += match_class.o
1717
lib-$(CONFIG_PLATFORM_MINGW32) += mntent.o
1818
lib-$(CONFIG_PLATFORM_MINGW32) += net.o
19+
lib-$(CONFIG_PLATFORM_MINGW32) += path-convert.o
1920
lib-$(CONFIG_PLATFORM_MINGW32) += poll.o
2021
lib-$(CONFIG_PLATFORM_MINGW32) += popen.o
2122
lib-$(CONFIG_PLATFORM_MINGW32) += regex.o

win32/env.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "libbb.h"
2+
#include "path-convert.h"
23

34
#undef getenv
45
#undef putenv
@@ -18,16 +19,20 @@ char *mingw_getenv(const char *name)
1819
int setenv(const char *name, const char *value, int replace)
1920
{
2021
int out;
21-
char *envstr;
22+
char *envstr, *to_free = NULL;
2223

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

29+
if (!strcmp(name, "PATH"))
30+
value = to_free = path_convert_path_list(value, PATH_CONVERT_WINDOWS);
31+
2832
envstr = xasprintf("%s=%s", name, value);
2933
out = mingw_putenv(envstr);
3034
free(envstr);
35+
free(to_free);
3136

3237
return out;
3338
}

win32/mingw.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ wchar_t *mingw_pathconv(const char *path)
303303
pseudo_root[pseudo_root_len++] = L'\\';
304304
}
305305

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

0 commit comments

Comments
 (0)