From 70da1e67eac8dd7eac22737effbbc87902a36a00 Mon Sep 17 00:00:00 2001 From: Manoj Ampalam Date: Mon, 6 Mar 2017 16:18:40 -0800 Subject: [PATCH] fgets and utf8 functions for Windows (#87) --- contrib/win32/openssh/libssh.vcxproj | 1 - contrib/win32/openssh/libssh.vcxproj.filters | 3 - contrib/win32/openssh/win32iocompat.vcxproj | 1 + .../openssh/win32iocompat.vcxproj.filters | 1 + contrib/win32/win32compat/inc/stdio.h | 7 +- contrib/win32/win32compat/misc.c | 69 +++++++++++++++ contrib/win32/win32compat/win32-utf8.c | 52 +++++++++++ scp.c | 20 ----- sftp.c | 87 +------------------ utf8.c | 12 +-- 10 files changed, 134 insertions(+), 119 deletions(-) create mode 100644 contrib/win32/win32compat/win32-utf8.c diff --git a/contrib/win32/openssh/libssh.vcxproj b/contrib/win32/openssh/libssh.vcxproj index 1205b58dc..16fff9615 100644 --- a/contrib/win32/openssh/libssh.vcxproj +++ b/contrib/win32/openssh/libssh.vcxproj @@ -283,7 +283,6 @@ - true diff --git a/contrib/win32/openssh/libssh.vcxproj.filters b/contrib/win32/openssh/libssh.vcxproj.filters index efb660e02..1959987d5 100644 --- a/contrib/win32/openssh/libssh.vcxproj.filters +++ b/contrib/win32/openssh/libssh.vcxproj.filters @@ -282,9 +282,6 @@ Source Files - - Source Files - Source Files diff --git a/contrib/win32/openssh/win32iocompat.vcxproj b/contrib/win32/openssh/win32iocompat.vcxproj index 6039af70c..71c9e7a7e 100644 --- a/contrib/win32/openssh/win32iocompat.vcxproj +++ b/contrib/win32/openssh/win32iocompat.vcxproj @@ -160,6 +160,7 @@ + diff --git a/contrib/win32/openssh/win32iocompat.vcxproj.filters b/contrib/win32/openssh/win32iocompat.vcxproj.filters index 07a88fa4c..eefef7c86 100644 --- a/contrib/win32/openssh/win32iocompat.vcxproj.filters +++ b/contrib/win32/openssh/win32iocompat.vcxproj.filters @@ -19,6 +19,7 @@ + diff --git a/contrib/win32/win32compat/inc/stdio.h b/contrib/win32/win32compat/inc/stdio.h index 83aa05b27..269ece845 100644 --- a/contrib/win32/win32compat/inc/stdio.h +++ b/contrib/win32/win32compat/inc/stdio.h @@ -5,6 +5,12 @@ FILE* w32_fopen_utf8(const char *, const char *); #define fopen w32_fopen_utf8 +char* w32_fgets(char *str, int n, FILE *stream); +#define fgets w32_fgets + +int w32_setvbuf(FILE *stream,char *buffer, int mode, size_t size); +#define setvbuf w32_setvbuf + /* stdio.h additional definitions */ #define popen _popen #define pclose _pclose @@ -14,4 +20,3 @@ FILE* w32_fdopen(int fd, const char *mode); int w32_rename(const char *old_name, const char *new_name); #define rename w32_rename - diff --git a/contrib/win32/win32compat/misc.c b/contrib/win32/win32compat/misc.c index 9e87efa1b..e74dd7160 100644 --- a/contrib/win32/win32compat/misc.c +++ b/contrib/win32/win32compat/misc.c @@ -276,6 +276,75 @@ w32_fopen_utf8(const char *path, const char *mode) return f; } +/* fgets to support Unicode input */ +char* + w32_fgets(char *str, int n, FILE *stream) { + HANDLE h = (HANDLE)_get_osfhandle(_fileno(stream)); + wchar_t* str_w = NULL; + char *ret = NULL, *str_tmp = NULL; + + if (h != NULL && h != INVALID_HANDLE_VALUE + && GetFileType(h) == FILE_TYPE_CHAR) { + /* + * read only n/4 wide chars from console + * each UTF-16 char may bloat upto 4 utf-8 chars when converted to utf-8 + * so we can fit in str[n] provided as input + */ + if ((str_w = malloc((n/4) * sizeof(wchar_t))) == NULL) { + errno = ENOMEM; + goto cleanup; + } + /* prepare for Unicode input */ + _setmode(_fileno(stream), O_U16TEXT); + if (fgetws(str_w, n/4, stream) == NULL) + goto cleanup; + if ((str_tmp = utf16_to_utf8(str_w)) == NULL) { + errno = ENOMEM; + goto cleanup; + } + if (strlen(str_tmp) > n - 1) { + /* shouldn't happen. but handling in case */ + errno = EINVAL; + goto cleanup; + } + memcpy(str, str_tmp, strlen(str_tmp) + 1); + ret = str; + } + else + ret = fgets(str, n, stream); +cleanup: + if (str_w) + free(str_w); + if (str_tmp) + free(str_tmp); + return ret; +} + +/* Account for differences between Unix's and Windows versions of setvbuf */ +int +w32_setvbuf(FILE *stream, char *buffer, int mode, size_t size) { + + /* BUG: setvbuf on console stream interferes with Unicode I/O */ + HANDLE h = (HANDLE)_get_osfhandle(_fileno(stream)); + + if (h != NULL && h != INVALID_HANDLE_VALUE + && GetFileType(h) == FILE_TYPE_CHAR) + return 0; + + /* BUG: setvbuf on file stream is interfering with w32_fopen */ + /* short circuit for now*/ + return 0; + + /* + * if size is 0, set no buffering. + * Windows does not differentiate __IOLBF and _IOFBF + */ + if (size == 0) + return setvbuf(stream, NULL, _IONBF, 0); + else + return setvbuf(stream, buffer, mode, size); +} + char * w32_programdir() { diff --git a/contrib/win32/win32compat/win32-utf8.c b/contrib/win32/win32compat/win32-utf8.c new file mode 100644 index 000000000..56fa236c6 --- /dev/null +++ b/contrib/win32/win32compat/win32-utf8.c @@ -0,0 +1,52 @@ +/* + * Temporary Windows versions of functions implemented in utf8.c + */ + #include +#include + +int +vfmprintf(FILE *f, const char *fmt, va_list list) +{ + return vfprintf(f, fmt, list); +} + +int +mprintf(const char *fmt, ...) +{ + int ret = 0; + va_list valist; + va_start(valist, fmt); + ret = vfmprintf(stdout, fmt, valist); + va_end(valist); + return ret; +} + +int +fmprintf(FILE *f, const char *fmt, ...) +{ + int ret = 0; + va_list valist; + va_start(valist, fmt); + ret = vfmprintf(f, fmt, valist); + va_end(valist); + return ret; +} + +int +snmprintf(char *buf, size_t len, int *written, const char *fmt, ...) +{ + int num; + va_list valist; + va_start(valist, fmt); + num = vsnprintf(buf, len, fmt, valist); + va_end(valist); + *written = num; + return 0; +} + +void +msetlocale(void) +{ + return; +} + diff --git a/scp.c b/scp.c index 8e734db2f..f6b9bc24c 100644 --- a/scp.c +++ b/scp.c @@ -1034,17 +1034,7 @@ rsource(char *name, struct stat *statp) (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", (u_int) (statp->st_mode & FILEMODEMASK), 0, last); if (verbose_mode) -#ifdef WINDOWS - /* TODO - make fmprintf work for Windows */ - { - printf("Entering directory: "); - wchar_t* wtmp = utf8_to_utf16(path); - WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), wtmp, wcslen(wtmp), 0, 0); - free(wtmp); - } -#else /* !WINDOWS */ fmprintf(stderr, "Entering directory: %s", path); -#endif /* !WINDOWS */ (void) atomicio(vwrite, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); @@ -1119,17 +1109,7 @@ sink(int argc, char **argv) } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); *cp = 0; if (verbose_mode) -#ifdef WINDOWS - /* TODO - make fmprintf work for Windows */ - { - printf("Sink: "); - wchar_t* wtmp = utf8_to_utf16(buf); - WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), wtmp, wcslen(wtmp), 0, 0); - free(wtmp); - } -#else /* !WINDOWS */ fmprintf(stderr, "Sink: %s", buf); -#endif /* !WINDOWS */ if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) { diff --git a/sftp.c b/sftp.c index 6510a0138..407703733 100644 --- a/sftp.c +++ b/sftp.c @@ -292,27 +292,6 @@ help(void) "? Synonym for help\n"); } -#ifdef WINDOWS -/* printf version to account for utf-8 input */ -/* TODO - merge this with vfmprint */ -static void printf_utf8(char *fmt, ... ) { - /* TODO - is 1024 sufficient */ - char buf[1024]; - int length = 0; - - va_list valist; - va_start(valist, fmt); - length = vsnprintf(buf, 1024, fmt, valist); - va_end(valist); - - write(STDOUT_FILENO, buf, length); -} - -/* override mprintf */ -#define mprintf(a,...) printf_utf8((a), __VA_ARGS__) -#define printf(a,...) printf_utf8((a), __VA_ARGS__) -#endif /* WINDOWS */ - static void local_do_shell(const char *args) { @@ -420,7 +399,7 @@ make_absolute(char *p, const char *pwd) p = abs_str; } - /* convert '\\' tp '/' */ + /* convert '\\' to '/' */ convertToForwardslash(p); /* Append "/" if needed to the absolute windows path */ @@ -925,23 +904,7 @@ do_ls_dir(struct sftp_conn *conn, const char *path, } else mprintf("%s\n", d[n]->longname); } else { -#ifdef WINDOWS - /* cannot use printf_utf8 becuase of width specification */ - /* printf_utf8 does not account for utf-16 based argument widths */ - char *p = NULL; - wchar_t buf[1024]; - wchar_t* wtmp = utf8_to_utf16(fname); - swprintf(buf, 1024, L"%-*s", colspace, wtmp); - - if ((p = utf16_to_utf8(buf)) == NULL) - continue; - - write(STDOUT_FILENO, p, strlen(p)); - free(wtmp); - free(p); -#else mprintf("%-*s", colspace, fname); -#endif if (c >= columns) { printf("\n"); c = 1; @@ -1025,23 +988,7 @@ do_globbed_ls(struct sftp_conn *conn, const char *path, mprintf("%s\n", lname); free(lname); } else { -#ifdef WINDOWS - /* cannot use printf_utf8 becuase of width specification */ - /* printf_utf8 does not account for utf-16 based argument widths */ - char *p = NULL; - wchar_t buf[1024]; - wchar_t* wtmp = utf8_to_utf16(fname); - swprintf(buf, 1024, L"%-*s", colspace, wtmp); - - if ((p = utf16_to_utf8(buf)) == NULL) - continue; - - write(STDOUT_FILENO, p, strlen(p)); - free(wtmp); - free(p); -#else mprintf("%-*s", colspace, fname); -#endif if (c >= columns) { printf("\n"); c = 1; @@ -2211,20 +2158,8 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2) interactive = !batchmode && isatty(STDIN_FILENO); err = 0; -#ifdef WINDOWS - /* Min buffer size allowed in Windows is 2*/ - setvbuf(stdout, NULL, _IOLBF, 2); - - /* We do this only in interactive mode as we are unable to read files with UTF8 BOM */ - if (interactive) { - setvbuf(infile, NULL, _IOLBF, 2); - _setmode(_fileno(stdin), O_U16TEXT); /* prepare for Unicode input */ - } -#else /* !WINDOWS */ setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(infile, NULL, _IOLBF, 0); -#endif /* !WINDOWS */ - for (;;) { char *cp; @@ -2232,25 +2167,6 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2) signal(SIGINT, SIG_IGN); if (el == NULL) { -#ifdef WINDOWS - /* fgets on Windows does not support Unicode input*/ - if (interactive) { - wchar_t wcmd[2048]; - printf("sftp> "); - if (fgetws(wcmd, sizeof(cmd)/sizeof(wchar_t), infile) == NULL) { - printf("\n"); - break; - } - else { - char *pcmd = NULL; - if ((pcmd = utf16_to_utf8(wcmd)) == NULL) - fatal("failed to convert input arguments"); - strcpy(cmd, pcmd); - free(pcmd); - } - } else if (fgets(cmd, sizeof(cmd), infile) == NULL) - break; -#else /* !WINDOWS */ if (interactive) printf("sftp> "); if (fgets(cmd, sizeof(cmd), infile) == NULL) { @@ -2258,7 +2174,6 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2) printf("\n"); break; } -#endif/* !WINDOWS */ if (!interactive) { /* Echo command */ mprintf("sftp> %s", cmd); if (strlen(cmd) > 0 && diff --git a/utf8.c b/utf8.c index 1f0dc44db..6fb05bb42 100644 --- a/utf8.c +++ b/utf8.c @@ -1,4 +1,4 @@ -/* $OpenBSD: utf8.c,v 1.4 2017/02/02 10:54:25 jsg Exp $ */ +/* $OpenBSD: utf8.c,v 1.5 2017/02/19 00:10:57 djm Exp $ */ /* * Copyright (c) 2016 Ingo Schwarze * @@ -57,16 +57,11 @@ static int vasnmprintf(char **, size_t, int *, const char *, va_list); static int dangerous_locale(void) { -#ifdef WINDOWS - wchar_t loc[LOCALE_NAME_MAX_LENGTH]; - GetSystemDefaultLocaleName(loc, LOCALE_NAME_MAX_LENGTH); - return wcscmp(loc, L"US-ASCII") && wcscmp(loc, L"UTF-8"); -#else /* !WINDOWS */ char *loc; loc = nl_langinfo(CODESET); - return strcmp(loc, "US-ASCII") && strcmp(loc, "UTF-8"); -#endif /* !WINDOWS */ + return strcmp(loc, "US-ASCII") != 0 && strcmp(loc, "UTF-8") != 0 && + strcmp(loc, "ANSI_X3.4-1968") != 0; } static int @@ -337,3 +332,4 @@ msetlocale(void) /* We can handle this locale */ setlocale(LC_CTYPE, ""); } +