4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-01-19 04:49:25 +08:00
newlib-cygwin/winsup/cygwin/smallprint.cc
Takashi Yano bb42852062 Cygwin: pty: Implement new pseudo console support.
- In this implementation, pseudo console is created for each native
  console app. Advantages and disadvantages of this implementation
  over the previous implementation are as follows.

  Advantages:
  1) No performance degradation in pty output for cygwin process.
      https://cygwin.com/pipermail/cygwin/2020-February/243858.html
  2) Free from the problem caused by difference of behaviour of control
     sequences between real terminal and pseudo console.
      https://cygwin.com/pipermail/cygwin/2019-December/243281.html
      https://cygwin.com/pipermail/cygwin/2020-February/243855.html
  3) Free from the problem in cgdb and emacs gud.
      https://cygwin.com/pipermail/cygwin/2020-January/243601.html
      https://cygwin.com/pipermail/cygwin/2020-March/244146.html
  4) Redrawing screen on executing native console apps is not necessary.
  5) cygwin-console-helper is not necessary for the pseudo console
     support.
  6) The codes for pseudo console support are much simpler than that
     of the previous one.

  Disadvantages:
  1) The cygwin program which calls console API directly does not work.
  2) The apps which use console API cannot be debugged with gdb. This
     is because pseudo console is not activated since gdb uses
     CreateProcess() rather than exec(). Even with this limitation,
     attaching gdb to native apps, in which pseudo console is already
     activated, works.
  3) Typeahead key inputs are discarded while native console app is
     executed. Simirally, typeahead key inputs while cygwin app is
     executed are not inherited to native console app.
  4) Code page cannot be changed by chcp.com. Acctually, chcp works
     itself and changes code page of its own pseudo console.  However,
     since pseudo console is recreated for another process, it cannot
     inherit the code page.
  5) system_printf() does not work after stderr is closed. (Same with
     cygwin 3.0.7)
  6) Startup time of native console apps is about 3 times slower than
     previous implemenation.
  7) Pseudo console cannot be activated if it is already activated for
     another process on same pty.
2020-08-22 13:43:49 +02:00

699 lines
14 KiB
C++

/* smallprint.cc: small print routines for WIN32
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "winsup.h"
#include "ntdll.h"
#include "sync.h"
#include <stdlib.h>
#include <ctype.h>
#include <wchar.h>
/*
Meaning of format conversion specifiers. If 'l' isn't explicitely mentioned,
it's ignored!
0 modifier: pad with zero instead of spaces
0-9 modifier: field length
+ modifier: always add sign
l modifier to d, o, R, u, x, y: 4 byte on 32 bit, 8 byte on 64 bit
l modifier to s, S, W: non-ASCII tweaking
_ modifier: print lower case hex chars a-f instead of A-F
c char
C WCHAR/wchar_t
d signed int, 4 byte
D signed long long, 8 byte
E GetLastError
o octal unsigned int, 4 byte
O octal unsigned long long, 8 byte
p address
P process name
R return value, 4 byte.
s char *
S PUNICODE_STRING
u unsigned int, 4 byte
U unsigned long long, 8 byte
W PWCHAR/wchar_t *
x hex unsigned int, 4 byte
X hex unsigned long long, 8 byte
y 0x hex unsigned int, 4 byte
Y 0x hex unsigned long long, 8 byte
*/
#define LLMASK (0xffffffffffffffffULL)
#define LMASK (0xffffffff)
#define rnarg(dst, base, dosign, len, pad) __rn ((dst), (base), (dosign), va_arg (ap, int32_t), len, pad, LMASK)
#define rnargLL(dst, base, dosign, len, pad) __rn ((dst), (base), (dosign), va_arg (ap, uint64_t), len, pad, LLMASK)
static const char hex_str_upper[] = "0123456789ABCDEF";
static const char hex_str_lower[] = "0123456789abcdef";
class tmpbuf
{
static WCHAR buf[NT_MAX_PATH];
static muto lock;
bool locked;
public:
operator WCHAR * ()
{
if (!locked)
{
lock.init ("smallprint_buf")->acquire ();
locked = true;
}
return buf;
}
operator char * () {return (char *) ((WCHAR *) *this);}
tmpbuf (): locked (false) {};
~tmpbuf ()
{
if (locked)
lock.release ();
}
};
WCHAR tmpbuf::buf[NT_MAX_PATH];
NO_COPY muto tmpbuf::lock;
static char __fastcall *
__rn (char *dst, int base, int dosign, long long val, int len, int pad, unsigned long long mask)
{
/* longest number is ULLONG_MAX, 18446744073709551615, 20 digits */
unsigned long long uval = 0;
char res[20];
int l = 0;
const char *hex_str;
if (base < 0)
{
base = -base;
hex_str = hex_str_lower;
}
else
hex_str = hex_str_upper;
if (dosign && val < 0)
{
*dst++ = '-';
uval = -val;
}
else if (dosign > 0 && val > 0)
{
*dst++ = '+';
uval = val;
}
else
uval = val;
uval &= mask;
do
{
res[l++] = hex_str[uval % base];
uval /= base;
}
while (uval);
while (len-- > l)
*dst++ = pad;
while (l > 0)
*dst++ = res[--l];
return dst;
}
extern "C" int
__small_vsprintf (char *dst, const char *fmt, va_list ap)
{
tmpbuf tmp;
char *orig = dst;
const char *s;
PWCHAR w;
UNICODE_STRING uw, *us;
int base = 0;
DWORD err = GetLastError ();
intptr_t Rval = 0;
while (*fmt)
{
int i, n = 0x7fff;
bool l_opt = false;
/* set to -1 on '_', indicates upper (1)/lower(-1) case */
int h_opt = 1;
const char *hex_str = hex_str_upper;
if (*fmt != '%')
*dst++ = *fmt++;
else
{
int len = 0;
char pad = ' ';
int addsign = -1;
switch (*++fmt)
{
case '+':
addsign = 1;
fmt++;
break;
case '%':
*dst++ = *fmt++;
continue;
}
for (;;)
{
char c = *fmt++;
switch (c)
{
case '0':
if (len == 0)
{
pad = '0';
continue;
}
fallthrough;
case '1' ... '9':
len = len * 10 + (c - '0');
continue;
case 'l':
l_opt = true;
continue;
case '_':
h_opt = -1;
hex_str = hex_str_lower;
continue;
case 'c':
{
unsigned char c = (va_arg (ap, int) & 0xff);
if (isprint (c) || pad != '0')
*dst++ = c;
else
{
*dst++ = '0';
*dst++ = 'x';
dst = __rn (dst, h_opt * 16, 0, c, len, pad, LMASK);
}
}
break;
case 'C':
{
WCHAR wc = (WCHAR) va_arg (ap, int);
char buf[MB_LEN_MAX+1] = "", *c;
sys_wcstombs (buf, MB_LEN_MAX+1, &wc, 1);
for (c = buf; *c; ++c)
*dst++ = *c;
}
break;
case 'E':
strcpy (dst, "Win32 error ");
dst = __rn (dst + sizeof ("Win32 error"), 10, 0, err, len, pad, LMASK);
break;
case 'R':
{
#ifdef __x86_64__
if (l_opt)
Rval = va_arg (ap, int64_t);
else
#endif
Rval = va_arg (ap, int32_t);
dst = __rn (dst, 10, addsign, Rval, len, pad, LMASK);
}
break;
case 'd':
base = 10;
#ifdef __x86_64__
if (l_opt)
goto gen_decimalLL;
#endif
goto gen_decimal;
case 'u':
base = 10;
addsign = 0;
#ifdef __x86_64__
if (l_opt)
goto gen_decimalLL;
#endif
goto gen_decimal;
case 'o':
base = 8;
addsign = 0;
#ifdef __x86_64__
if (l_opt)
goto gen_decimalLL;
#endif
goto gen_decimal;
case 'y':
*dst++ = '0';
*dst++ = 'x';
fallthrough;
case 'x':
base = 16;
addsign = 0;
#ifdef __x86_64__
if (l_opt)
goto gen_decimalLL;
#endif
gen_decimal:
dst = rnarg (dst, h_opt * base, addsign, len, pad);
break;
case 'D':
base = 10;
goto gen_decimalLL;
case 'U':
base = 10;
addsign = 0;
goto gen_decimalLL;
case 'O':
base = 8;
addsign = 0;
goto gen_decimalLL;
case 'Y':
*dst++ = '0';
*dst++ = 'x';
fallthrough;
case 'X':
base = 16;
addsign = 0;
gen_decimalLL:
dst = rnargLL (dst, h_opt * base, addsign, len, pad);
break;
case 'p':
*dst++ = '0';
*dst++ = 'x';
#ifdef __x86_64__
dst = rnargLL (dst, h_opt * 16, 0, len, pad);
#else
dst = rnarg (dst, h_opt * 16, 0, len, pad);
#endif
break;
case '.':
n = strtol (fmt, (char **) &fmt, 10);
if (*fmt++ != 's')
goto endfor;
fallthrough;
case 's':
s = va_arg (ap, char *);
if (s == NULL)
s = "(null)";
fillin:
for (i = 0; *s && i < n; i++)
if (l_opt && ((*(unsigned char *)s <= 0x1f && *s != '\n')
|| *(unsigned char *)s >= 0x7f))
{
*dst++ = '\\';
*dst++ = 'x';
*dst++ = hex_str[*(unsigned char *)s >> 4];
*dst++ = hex_str[*(unsigned char *)s++ & 0xf];
}
else
*dst++ = *s++;
break;
case 'P':
RtlInitUnicodeString (us = &uw, global_progname);
goto wfillin;
case 'W':
w = va_arg (ap, PWCHAR);
RtlInitUnicodeString (us = &uw, w ?: L"(null)");
goto wfillin;
case 'S':
us = va_arg (ap, PUNICODE_STRING);
if (!us)
RtlInitUnicodeString (us = &uw, L"(null)");
wfillin:
if (l_opt)
{
for (USHORT i = 0; i < us->Length / sizeof (WCHAR); ++i)
{
WCHAR w = us->Buffer[i];
if ((w <= 0x1f && w != '\n') || w >= 0x7f)
{
*dst++ = '\\';
*dst++ = 'x';
*dst++ = hex_str[(w >> 12) & 0xf];
*dst++ = hex_str[(w >> 8) & 0xf];
*dst++ = hex_str[(w >> 4) & 0xf];
*dst++ = hex_str[w & 0xf];
}
else
*dst++ = w;
}
}
else if (sys_wcstombs (tmp, NT_MAX_PATH, us->Buffer,
us->Length / sizeof (WCHAR)))
{
s = tmp;
goto fillin;
}
break;
default:
*dst++ = '?';
*dst++ = fmt[-1];
}
endfor:
break;
}
}
}
if (Rval < 0)
{
dst = stpcpy (dst, ", errno ");
dst = __rn (dst, 10, false, get_errno (), 0, 0, LMASK);
}
*dst = 0;
SetLastError (err);
return dst - orig;
}
extern "C" int
__small_sprintf (char *dst, const char *fmt, ...)
{
int r;
va_list ap;
va_start (ap, fmt);
r = __small_vsprintf (dst, fmt, ap);
va_end (ap);
return r;
}
void
small_printf (const char *fmt, ...)
{
char buf[16384];
va_list ap;
DWORD done;
int count;
#if 0 /* Turn on to force console errors */
extern SECURITY_ATTRIBUTES sec_none;
HANDLE h = CreateFileA ("CONOUT$", GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_WRITE, &sec_none,
OPEN_EXISTING, 0, 0);
if (h)
SetStdHandle (STD_ERROR_HANDLE, h);
#endif
va_start (ap, fmt);
count = __small_vsprintf (buf, fmt, ap);
va_end (ap);
WriteFile (GetStdHandle (STD_ERROR_HANDLE), buf, count, &done, NULL);
FlushFileBuffers (GetStdHandle (STD_ERROR_HANDLE));
}
#ifdef DEBUGGING
static HANDLE NO_COPY console_handle = NULL;
void
console_printf (const char *fmt, ...)
{
char buf[16384];
va_list ap;
DWORD done;
int count;
if (!console_handle)
console_handle = CreateFileA ("CON", GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, 0);
if (console_handle == INVALID_HANDLE_VALUE)
console_handle = GetStdHandle (STD_ERROR_HANDLE);
va_start (ap, fmt);
count = __small_vsprintf (buf, fmt, ap);
va_end (ap);
WriteFile (console_handle, buf, count, &done, NULL);
FlushFileBuffers (console_handle);
}
#endif
#define wrnarg(dst, base, dosign, len, pad) __wrn ((dst), (base), (dosign), va_arg (ap, long), len, pad, LMASK)
#define wrnargLL(dst, base, dosign, len, pad) __wrn ((dst), (base), (dosign), va_arg (ap, unsigned long long), len, pad, LLMASK)
static PWCHAR __fastcall
__wrn (PWCHAR dst, int base, int dosign, long long val, int len, int pad, unsigned long long mask)
{
/* longest number is ULLONG_MAX, 18446744073709551615, 20 digits */
unsigned long long uval = 0;
WCHAR res[20];
int l = 0;
const char *hex_str;
if (base < 0)
{
base = -base;
hex_str = hex_str_lower;
}
else
hex_str = hex_str_upper;
if (dosign && val < 0)
{
*dst++ = L'-';
uval = -val;
}
else if (dosign > 0 && val > 0)
{
*dst++ = L'+';
uval = val;
}
else
uval = val;
uval &= mask;
do
{
res[l++] = hex_str[uval % base];
uval /= base;
}
while (uval);
while (len-- > l)
*dst++ = pad;
while (l > 0)
*dst++ = res[--l];
return dst;
}
int
__small_vswprintf (PWCHAR dst, const WCHAR *fmt, va_list ap)
{
tmpbuf tmp;
PWCHAR orig = dst;
const char *s;
PWCHAR w;
UNICODE_STRING uw, *us;
int base = 0;
DWORD err = GetLastError ();
intptr_t Rval = 0;
while (*fmt)
{
unsigned int n = 0x7fff;
#ifdef __x86_64__
bool l_opt = false;
#endif
/* set to -1 on '_', indicates upper (1)/lower(-1) case */
int h_opt = 1;
if (*fmt != L'%')
*dst++ = *fmt++;
else
{
int len = 0;
WCHAR pad = L' ';
int addsign = -1;
switch (*++fmt)
{
case L'+':
addsign = 1;
fmt++;
break;
case L'%':
*dst++ = *fmt++;
continue;
}
for (;;)
{
char c = *fmt++;
switch (c)
{
case L'0':
if (len == 0)
{
pad = L'0';
continue;
}
fallthrough;
case L'1' ... L'9':
len = len * 10 + (c - L'0');
continue;
case L'l':
#ifdef __x86_64__
l_opt = true;
#endif
continue;
case '_':
h_opt = -1;
continue;
case L'c':
case L'C':
*dst++ = va_arg (ap, unsigned);
break;
case L'E':
wcscpy (dst, L"Win32 error ");
dst = __wrn (dst + sizeof ("Win32 error"), 10, 0, err, len, pad, LMASK);
break;
case 'R':
{
#ifdef __x86_64__
if (l_opt)
Rval = va_arg (ap, int64_t);
else
#endif
Rval = va_arg (ap, int32_t);
dst = __wrn (dst, 10, addsign, Rval, len, pad, LMASK);
}
break;
case L'd':
base = 10;
#ifdef __x86_64__
if (l_opt)
goto gen_decimalLL;
#endif
goto gen_decimal;
case 'u':
base = 10;
addsign = 0;
#ifdef __x86_64__
if (l_opt)
goto gen_decimalLL;
#endif
goto gen_decimal;
case 'o':
base = 8;
addsign = 0;
#ifdef __x86_64__
if (l_opt)
goto gen_decimalLL;
#endif
goto gen_decimal;
case 'y':
*dst++ = '0';
*dst++ = 'x';
fallthrough;
case 'x':
base = 16;
addsign = 0;
#ifdef __x86_64__
if (l_opt)
goto gen_decimalLL;
#endif
gen_decimal:
dst = wrnarg (dst, h_opt * base, addsign, len, pad);
break;
case 'D':
base = 10;
goto gen_decimalLL;
case 'U':
base = 10;
addsign = 0;
goto gen_decimalLL;
case 'O':
base = 8;
addsign = 0;
goto gen_decimalLL;
case 'Y':
*dst++ = '0';
*dst++ = 'x';
fallthrough;
case 'X':
base = 16;
addsign = 0;
gen_decimalLL:
dst = wrnargLL (dst, h_opt * base, addsign, len, pad);
break;
case L'p':
*dst++ = L'0';
*dst++ = L'x';
#ifdef __x86_64__
dst = wrnargLL (dst, h_opt * 16, 0, len, pad);
#else
dst = wrnarg (dst, h_opt * 16, 0, len, pad);
#endif
break;
case L'P':
RtlInitUnicodeString (us = &uw, global_progname);
goto fillin;
case L'.':
n = wcstoul (fmt, (wchar_t **) &fmt, 10);
if (*fmt++ != L's')
goto endfor;
fallthrough;
case L's':
s = va_arg (ap, char *);
if (s == NULL)
s = "(null)";
sys_mbstowcs (tmp, NT_MAX_PATH, s, n < 0x7fff ? (int) n : -1);
RtlInitUnicodeString (us = &uw, tmp);
goto fillin;
break;
case L'W':
w = va_arg (ap, PWCHAR);
RtlInitUnicodeString (us = &uw, w ?: L"(null)");
goto fillin;
case L'S':
us = va_arg (ap, PUNICODE_STRING);
if (!us)
RtlInitUnicodeString (us = &uw, L"(null)");
fillin:
if (us->Length / sizeof (WCHAR) < n)
n = us->Length / sizeof (WCHAR);
w = us->Buffer;
for (unsigned int i = 0; i < n; i++)
*dst++ = *w++;
break;
default:
*dst++ = L'?';
*dst++ = fmt[-1];
}
endfor:
break;
}
}
}
if (Rval < 0)
{
dst = wcpcpy (dst, L", errno ");
dst = __wrn (dst, 10, false, get_errno (), 0, 0, LMASK);
}
*dst = L'\0';
SetLastError (err);
return dst - orig;
}
int
__small_swprintf (PWCHAR dst, const WCHAR *fmt, ...)
{
int r;
va_list ap;
va_start (ap, fmt);
r = __small_vswprintf (dst, fmt, ap);
va_end (ap);
return r;
}