83b2d576c8
- API hook used for pseudo console support causes slow down. This patch limits API hook to only program which is linked with the corresponding APIs. Normal cygwin program is not linked with such APIs (such as WriteFile, etc...) directly, therefore, no slow down occurs. However, console access by cygwin.dll itself cannot switch the r/w pipe to pseudo console side. Therefore, the code to switch it forcely to pseudo console side is added to smallprint.cc and strace.cc.
701 lines
14 KiB
C++
701 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
/*FALLTHRU*/
|
|
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';
|
|
/*FALLTHRU*/
|
|
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';
|
|
/*FALLTHRU*/
|
|
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;
|
|
/*FALLTHRU*/
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
set_ishybrid_and_switch_to_pcon (GetStdHandle (STD_ERROR_HANDLE));
|
|
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);
|
|
|
|
set_ishybrid_and_switch_to_pcon (console_handle);
|
|
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;
|
|
}
|
|
/*FALLTHRU*/
|
|
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';
|
|
/*FALLTHRU*/
|
|
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';
|
|
/*FALLTHRU*/
|
|
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;
|
|
/*FALLTHRU*/
|
|
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;
|
|
}
|