From 803e389cdb9c1254934c107b0dcddbfd9821cd0a Mon Sep 17 00:00:00 2001 From: Reini Urban Date: Tue, 12 Apr 2011 12:19:36 +0200 Subject: CYG17 utf8 paths Use cygwin_conv_path since cygwin-1.7 and support native utf-8 paths. --- cygwin/cygwin.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 218 insertions(+), 34 deletions(-) (limited to 'cygwin') diff --git a/cygwin/cygwin.c b/cygwin/cygwin.c index aa6938d279..0f5fe1d7fc 100644 --- a/cygwin/cygwin.c +++ b/cygwin/cygwin.c @@ -10,9 +10,13 @@ #include #include #include +#include #include #include #include +#if (CYGWIN_VERSION_API_MINOR >= 181) +#include +#endif /* * pp_system() implemented via spawn() @@ -125,7 +129,7 @@ do_spawn (char *cmd) return do_spawnvp("sh",command); } - Newx (PL_Argv,(s-cmd)/2+2,const char*); + Newx (PL_Argv,(s-cmd)/2+2,char*); PL_Cmd=savepvn (cmd,s-cmd); a=PL_Argv; for (s=PL_Cmd; *s;) { @@ -143,6 +147,44 @@ do_spawn (char *cmd) return do_spawnvp(PL_Argv[0],(const char * const *)PL_Argv); } +#if (CYGWIN_VERSION_API_MINOR >= 181) +char* +wide_to_utf8(const wchar_t *wbuf) +{ + char *buf; + int wlen = 0; + char *oldlocale = setlocale(LC_CTYPE, NULL); + setlocale(LC_CTYPE, "utf-8"); + + /* uvuni_to_utf8(buf, chr) or Encoding::_bytes_to_utf8(sv, "UCS-2BE"); */ + wlen = wcsrtombs(NULL, (const wchar_t **)&wbuf, wlen, NULL); + buf = (char *) safemalloc(wlen+1); + wcsrtombs(buf, (const wchar_t **)&wbuf, wlen, NULL); + + if (oldlocale) setlocale(LC_CTYPE, oldlocale); + else setlocale(LC_CTYPE, "C"); + return buf; +} + +wchar_t* +utf8_to_wide(const char *buf) +{ + wchar_t *wbuf; + mbstate_t mbs; + char *oldlocale = setlocale(LC_CTYPE, NULL); + int wlen = sizeof(wchar_t)*strlen(buf); + + setlocale(LC_CTYPE, "utf-8"); + wbuf = (wchar_t *) safemalloc(wlen); + /* utf8_to_uvuni(pathname, wpath) or Encoding::_utf8_to_bytes(sv, "UCS-2BE"); */ + wlen = mbsrtowcs(wbuf, (const char**)&buf, wlen, &mbs); + + if (oldlocale) setlocale(LC_CTYPE, oldlocale); + else setlocale(LC_CTYPE, "C"); + return wbuf; +} +#endif /* cygwin 1.7 */ + /* see also Cwd.pm */ XS(Cygwin_cwd) { @@ -194,7 +236,12 @@ XS(XS_Cygwin_winpid_to_pid) pid = (pid_t)SvIV(ST(0)); - if ((RETVAL = cygwin32_winpid_to_pid(pid)) > 0) { +#if (CYGWIN_VERSION_API_MINOR >= 181) + RETVAL = cygwin_winpid_to_pid(pid); +#else + RETVAL = cygwin32_winpid_to_pid(pid); +#endif + if (RETVAL > 0) { XSprePUSH; PUSHi((IV)RETVAL); XSRETURN(1); } @@ -202,34 +249,91 @@ XS(XS_Cygwin_winpid_to_pid) } XS(XS_Cygwin_win_to_posix_path) + { dXSARGS; int absolute_flag = 0; STRLEN len; int err; - char *pathname, *buf; + char *src_path; + char *posix_path; + int isutf8 = 0; if (items < 1 || items > 2) Perl_croak(aTHX_ "Usage: Cygwin::win_to_posix_path(pathname, [absolute])"); - pathname = SvPV(ST(0), len); + src_path = SvPV(ST(0), len); if (items == 2) absolute_flag = SvTRUE(ST(1)); if (!len) Perl_croak(aTHX_ "can't convert empty path"); - buf = (char *) safemalloc (len + 260 + 1001); + isutf8 = SvUTF8(ST(0)); +#if (CYGWIN_VERSION_API_MINOR >= 181) + /* Check utf8 flag and use wide api then. + Size calculation: On overflow let cygwin_conv_path calculate the final size. + */ + if (isutf8) { + int what = absolute_flag ? CCP_WIN_W_TO_POSIX : CCP_WIN_W_TO_POSIX | CCP_RELATIVE; + int wlen = sizeof(wchar_t)*(len + 260 + 1001); + wchar_t *wpath = (wchar_t *) safemalloc(sizeof(wchar_t)*len); + wchar_t *wbuf = (wchar_t *) safemalloc(wlen); + if (!IN_BYTES) { + mbstate_t mbs; + char *oldlocale = setlocale(LC_CTYPE, NULL); + setlocale(LC_CTYPE, "utf-8"); + /* utf8_to_uvuni(src_path, wpath) or Encoding::_utf8_to_bytes(sv, "UCS-2BE"); */ + wlen = mbsrtowcs(wpath, (const char**)&src_path, wlen, &mbs); + if (wlen > 0) + err = cygwin_conv_path(what, wpath, wbuf, wlen); + if (oldlocale) setlocale(LC_CTYPE, oldlocale); + else setlocale(LC_CTYPE, "C"); + } else { /* use bytes; assume already ucs-2 encoded bytestream */ + err = cygwin_conv_path(what, src_path, wbuf, wlen); + } + if (err == ENOSPC) { /* our space assumption was wrong, not enough space */ + int newlen = cygwin_conv_path(what, wpath, wbuf, 0); + wbuf = (wchar_t *) realloc(&wbuf, newlen); + err = cygwin_conv_path(what, wpath, wbuf, newlen); + wlen = newlen; + } + /* utf16_to_utf8(*p, *d, bytlen, *newlen) */ + posix_path = (char *) safemalloc(wlen*3); + Perl_utf16_to_utf8(aTHX_ (U8*)&wpath, (U8*)posix_path, (I32)wlen*2, (I32*)&len); + /* + wlen = wcsrtombs(NULL, (const wchar_t **)&wbuf, wlen, NULL); + posix_path = (char *) safemalloc(wlen+1); + wcsrtombs(posix_path, (const wchar_t **)&wbuf, wlen, NULL); + */ + } else { + int what = absolute_flag ? CCP_WIN_A_TO_POSIX : CCP_WIN_A_TO_POSIX | CCP_RELATIVE; + posix_path = (char *) safemalloc (len + 260 + 1001); + err = cygwin_conv_path(what, src_path, posix_path, len + 260 + 1001); + if (err == ENOSPC) { /* our space assumption was wrong, not enough space */ + int newlen = cygwin_conv_path(what, src_path, posix_path, 0); + posix_path = (char *) realloc(&posix_path, newlen); + err = cygwin_conv_path(what, src_path, posix_path, newlen); + } + } +#else + posix_path = (char *) safemalloc (len + 260 + 1001); if (absolute_flag) - err = cygwin_conv_to_full_posix_path(pathname, buf); + err = cygwin_conv_to_full_posix_path(src_path, posix_path); else - err = cygwin_conv_to_posix_path(pathname, buf); + err = cygwin_conv_to_posix_path(src_path, posix_path); +#endif if (!err) { - ST(0) = sv_2mortal(newSVpv(buf, 0)); - safefree(buf); - XSRETURN(1); + EXTEND(SP, 1); + ST(0) = sv_2mortal(newSVpv(posix_path, 0)); + if (isutf8) { /* src was utf-8, so result should also */ + /* TODO: convert ANSI (local windows encoding) to utf-8 on cygwin-1.5 */ + SvUTF8_on(ST(0)); + } + safefree(posix_path); + XSRETURN(1); } else { - safefree(buf); + safefree(posix_path); XSRETURN_UNDEF; } } @@ -240,29 +344,80 @@ XS(XS_Cygwin_posix_to_win_path) int absolute_flag = 0; STRLEN len; int err; - char *pathname, *buf; + char *src_path, *win_path; + int isutf8 = 0; if (items < 1 || items > 2) Perl_croak(aTHX_ "Usage: Cygwin::posix_to_win_path(pathname, [absolute])"); - pathname = SvPV(ST(0), len); + src_path = SvPVx(ST(0), len); if (items == 2) absolute_flag = SvTRUE(ST(1)); if (!len) Perl_croak(aTHX_ "can't convert empty path"); - buf = (char *) safemalloc(len + 260 + 1001); - + isutf8 = SvUTF8(ST(0)); +#if (CYGWIN_VERSION_API_MINOR >= 181) + /* Check utf8 flag and use wide api then. + Size calculation: On overflow let cygwin_conv_path calculate the final size. + */ + if (isutf8) { + int what = absolute_flag ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_W | CCP_RELATIVE; + int wlen = sizeof(wchar_t)*(len + 260 + 1001); + wchar_t *wpath = (wchar_t *) safemalloc(sizeof(wchar_t)*len); + wchar_t *wbuf = (wchar_t *) safemalloc(wlen); + char *oldlocale = setlocale(LC_CTYPE, NULL); + setlocale(LC_CTYPE, "utf-8"); + if (!IN_BYTES) { + mbstate_t mbs; + /* utf8_to_uvuni(src_path, wpath) or Encoding::_utf8_to_bytes(sv, "UCS-2BE"); */ + wlen = mbsrtowcs(wpath, (const char**)&src_path, wlen, &mbs); + if (wlen > 0) + err = cygwin_conv_path(what, wpath, wbuf, wlen); + } else { /* use bytes; assume already ucs-2 encoded bytestream */ + err = cygwin_conv_path(what, src_path, wbuf, wlen); + } + if (err == ENOSPC) { /* our space assumption was wrong, not enough space */ + int newlen = cygwin_conv_path(what, wpath, wbuf, 0); + wbuf = (wchar_t *) realloc(&wbuf, newlen); + err = cygwin_conv_path(what, wpath, wbuf, newlen); + wlen = newlen; + } + /* also see utf8.c: Perl_utf16_to_utf8() or Encoding::_bytes_to_utf8(sv, "UCS-2BE"); */ + wlen = wcsrtombs(NULL, (const wchar_t **)&wbuf, wlen, NULL); + win_path = (char *) safemalloc(wlen+1); + wcsrtombs(win_path, (const wchar_t **)&wbuf, wlen, NULL); + if (oldlocale) setlocale(LC_CTYPE, oldlocale); + else setlocale(LC_CTYPE, "C"); + } else { + int what = absolute_flag ? CCP_POSIX_TO_WIN_A : CCP_POSIX_TO_WIN_A | CCP_RELATIVE; + win_path = (char *) safemalloc(len + 260 + 1001); + err = cygwin_conv_path(what, src_path, win_path, len + 260 + 1001); + if (err == ENOSPC) { /* our space assumption was wrong, not enough space */ + int newlen = cygwin_conv_path(what, src_path, win_path, 0); + win_path = (char *) realloc(&win_path, newlen); + err = cygwin_conv_path(what, src_path, win_path, newlen); + } + } +#else + if (isutf8) + Perl_warn(aTHX_ "can't convert utf8 path"); + win_path = (char *) safemalloc(len + 260 + 1001); if (absolute_flag) - err = cygwin_conv_to_full_win32_path(pathname, buf); + err = cygwin_conv_to_full_win32_path(src_path, win_path); else - err = cygwin_conv_to_win32_path(pathname, buf); + err = cygwin_conv_to_win32_path(src_path, win_path); +#endif if (!err) { - ST(0) = sv_2mortal(newSVpv(buf, 0)); - safefree(buf); - XSRETURN(1); + EXTEND(SP, 1); + ST(0) = sv_2mortal(newSVpv(win_path, 0)); + if (isutf8) { + SvUTF8_on(ST(0)); + } + safefree(win_path); + XSRETURN(1); } else { - safefree(buf); + safefree(win_path); XSRETURN_UNDEF; } } @@ -293,24 +448,22 @@ XS(XS_Cygwin_mount_flags) { dXSARGS; char *pathname; - char flags[260]; + char flags[PATH_MAX]; + flags[0] = '\0'; if (items != 1) - Perl_croak(aTHX_ "Usage: Cygwin::mount_flags(mnt_dir|'/cygwin')"); + Perl_croak(aTHX_ "Usage: Cygwin::mount_flags( mnt_dir | '/cygdrive' )"); pathname = SvPV_nolen(ST(0)); - /* TODO: Check for cygdrive registry setting, - * and then use CW_GET_CYGDRIVE_INFO - */ if (!strcmp(pathname, "/cygdrive")) { - char user[260]; - char system[260]; - char user_flags[260]; - char system_flags[260]; + char user[PATH_MAX]; + char system[PATH_MAX]; + char user_flags[PATH_MAX]; + char system_flags[PATH_MAX]; - cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system, user_flags, - system_flags); + cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system, + user_flags, system_flags); if (strlen(user) > 0) { sprintf(flags, "%s,cygdrive,%s", user_flags, user); @@ -323,6 +476,7 @@ XS(XS_Cygwin_mount_flags) } else { struct mntent *mnt; + int found = 0; setmntent (0, 0); while ((mnt = getmntent (0))) { if (!strcmp(pathname, mnt->mnt_dir)) { @@ -331,12 +485,42 @@ XS(XS_Cygwin_mount_flags) strcat(flags, ","); strcat(flags, mnt->mnt_opts); } + found++; break; } } endmntent (0); - ST(0) = sv_2mortal(newSVpv(flags, 0)); - XSRETURN(1); + + /* Check if arg is the current volume moint point if not default, + * and then use CW_GET_CYGDRIVE_INFO also. + */ + if (!found) { + char user[PATH_MAX]; + char system[PATH_MAX]; + char user_flags[PATH_MAX]; + char system_flags[PATH_MAX]; + + cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system, + user_flags, system_flags); + + if (strlen(user) > 0) { + if (strcmp(user,pathname)) { + sprintf(flags, "%s,cygdrive,%s", user_flags, user); + found++; + } + } else { + if (strcmp(user,pathname)) { + sprintf(flags, "%s,cygdrive,%s", system_flags, system); + found++; + } + } + } + if (found) { + ST(0) = sv_2mortal(newSVpv(flags, 0)); + XSRETURN(1); + } else { + XSRETURN_UNDEF; + } } } -- cgit v1.2.1