Skip to content

Commit 858bed2

Browse files
authored
Merge pull request #254 from VincentBlondeau/addStdoutIsConsolePrimitive
Add accurate check for console stdio + add associated primitive Note that this is only the Windows version. I'll add the Unix version in the next couple of days. Cheers, Alistair
2 parents 6bac0c6 + 82d6abc commit 858bed2

File tree

6 files changed

+247
-66
lines changed

6 files changed

+247
-66
lines changed

platforms/Cross/plugins/FilePlugin/FilePlugin.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ sqInt sqFileSync(SQFile *f);
6767
sqInt sqFileTruncate(SQFile *f,squeakFileOffsetType offset);
6868
sqInt sqFileThisSession(void);
6969
sqInt sqFileStdioHandlesInto(SQFile files[3]);
70+
sqInt sqFileDescriptorType(int fdNum);
7071

7172
/* directories */
7273

platforms/Cross/plugins/FilePlugin/sqFilePluginBasicPrims.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,23 @@ sqFileStdioHandlesInto(SQFile files[])
570570
return 7;
571571
}
572572

573+
574+
/*
575+
* Allow to test if the standard input/output files are from a console or not
576+
* Return values:
577+
* -1 - Error
578+
* 0 - no console (windows only)
579+
* 1 - normal terminal (unix terminal / windows console)
580+
* 2 - pipe
581+
* 3 - file
582+
* 4 - cygwin terminal (windows only)
583+
*/
584+
sqInt sqFileDescriptorType(int fdNum) {
585+
/* TODO Implement the unix version */
586+
return isatty(fdNum);
587+
}
588+
589+
573590
size_t
574591
sqFileReadIntoAt(SQFile *f, size_t count, char *byteArrayIndex, size_t startIndex) {
575592
/* Read count bytes from the given file into byteArray starting at

platforms/RiscOS/plugins/FilePlugin/sqFilePluginBasicPrims.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,11 @@ sqInt sqFileStdioHandlesInto(SQFile files[3]) {
407407
return 0;
408408
}
409409

410+
sqInt sqFileDescriptorType(int fdNum) {
411+
//Not implemented
412+
return -1;
413+
}
414+
410415
size_t sqFileReadIntoAt(SQFile *f, size_t count, char* byteArrayIndex, size_t startIndex) {
411416
/* Read count bytes from the given file into byteArray starting at
412417
* startIndex. byteArray is the address of the first byte of a

platforms/win32/plugins/FilePlugin/sqWin32FilePrims.c

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* (Max Leske)
2424
*
2525
*****************************************************************************/
26+
2627
#include <windows.h>
2728
#include <malloc.h>
2829
#include "sq.h"
@@ -285,8 +286,15 @@ sqConnectToFile(SQFile *sqFile, void *file, sqInt writeFlag)
285286
return 1;
286287
}
287288

288-
289-
289+
void
290+
sqFileStdioHandlesIntoFile_WithHandle_IsWritable(SQFile * file, HANDLE handle, int isWritable) {
291+
file->sessionID = thisSession;
292+
file->file = handle;
293+
file->writable = isWritable;
294+
file->lastOp = 0; /* unused on win32 */
295+
file->isStdioStream = isFileHandleATTY(handle);
296+
AddHandleToTable(win32Files, handle);
297+
}
290298

291299
/*
292300
* Fill-in files with handles for stdin, stdout and seterr as available and
@@ -296,32 +304,28 @@ sqConnectToFile(SQFile *sqFile, void *file, sqInt writeFlag)
296304
sqInt
297305
sqFileStdioHandlesInto(SQFile files[3])
298306
{
299-
DWORD mode;
300-
301-
files[0].sessionID = thisSession;
302-
files[0].file = GetStdHandle(STD_INPUT_HANDLE);
303-
files[0].writable = false;
304-
files[0].lastOp = 0; /* unused on win32 */
305-
files[0].isStdioStream = GetConsoleMode(files[0].file, &mode) != 0;
306-
AddHandleToTable(win32Files, files[0].file);
307-
308-
files[1].sessionID = thisSession;
309-
files[1].file = GetStdHandle(STD_OUTPUT_HANDLE);
310-
files[1].writable = true;
311-
files[1].lastOp = 0; /* unused on win32 */
312-
files[1].isStdioStream = GetConsoleMode(files[1].file, &mode) != 0;
313-
AddHandleToTable(win32Files, files[1].file);
314-
315-
files[2].sessionID = thisSession;
316-
files[2].file = GetStdHandle(STD_ERROR_HANDLE);
317-
files[2].writable = true;
318-
files[2].lastOp = 0; /* unused on win32 */
319-
files[2].isStdioStream = GetConsoleMode(files[2].file, &mode) != 0;
320-
AddHandleToTable(win32Files, files[2].file);
307+
sqFileStdioHandlesIntoFile_WithHandle_IsWritable(&files[0], GetStdHandle(STD_INPUT_HANDLE), false);
308+
sqFileStdioHandlesIntoFile_WithHandle_IsWritable(&files[1], GetStdHandle(STD_OUTPUT_HANDLE), true);
309+
sqFileStdioHandlesIntoFile_WithHandle_IsWritable(&files[2], GetStdHandle(STD_ERROR_HANDLE), true);
321310

322311
return 7;
323312
}
324313

314+
315+
/*
316+
* Allow to test if the standard input/output files are from a console or not
317+
* Return values:
318+
* -1 - Error
319+
* 0 - no console (windows only)
320+
* 1 - normal terminal (unix terminal / windows console)
321+
* 2 - pipe
322+
* 3 - file
323+
* 4 - cygwin terminal (windows only)
324+
*/
325+
sqInt sqFileDescriptorType(int fdNum) {
326+
return fileHandleType(_get_osfhandle(fdNum));
327+
}
328+
325329
size_t sqFileReadIntoAt(SQFile *f, size_t count, char* byteArrayIndex, size_t startIndex) {
326330
/* Read count bytes from the given file into byteArray starting at
327331
startIndex. byteArray is the address of the first byte of a

platforms/win32/vm/sqWin32Main.c

Lines changed: 131 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* with Unicode support.
1313
*****************************************************************************/
1414
#include <windows.h>
15+
#include <errno.h>
1516
#include <stdio.h>
1617
#include <stdlib.h>
1718
#include <string.h>
@@ -36,6 +37,42 @@
3637
# endif
3738
#endif
3839

40+
41+
/************************************************************************************************************/
42+
/* few addtional definitions for those having older include files especially #include <fileextd.h> */
43+
/************************************************************************************************************/
44+
#if (WINVER < 0x0600)
45+
/*Copied from winbase.h*/
46+
typedef struct _FILE_NAME_INFO {
47+
DWORD FileNameLength;
48+
WCHAR FileName[1];
49+
} FILE_NAME_INFO, *PFILE_NAME_INFO;
50+
typedef enum _FILE_INFO_BY_HANDLE_CLASS {
51+
FileBasicInfo = 0,
52+
FileStandardInfo = 1,
53+
FileNameInfo = 2,
54+
FileRenameInfo = 3,
55+
FileDispositionInfo = 4,
56+
FileAllocationInfo = 5,
57+
FileEndOfFileInfo = 6,
58+
FileStreamInfo = 7,
59+
FileCompressionInfo = 8,
60+
FileAttributeTagInfo = 9,
61+
FileIdBothDirectoryInfo = 10, // 0xA
62+
FileIdBothDirectoryRestartInfo = 11, // 0xB
63+
FileIoPriorityHintInfo = 12, // 0xC
64+
FileRemoteProtocolInfo = 13, // 0xD
65+
FileFullDirectoryInfo = 14, // 0xE
66+
FileFullDirectoryRestartInfo = 15, // 0xF
67+
FileStorageInfo = 16, // 0x10
68+
FileAlignmentInfo = 17, // 0x11
69+
FileIdInfo = 18, // 0x12
70+
FileIdExtdDirectoryInfo = 19, // 0x13
71+
FileIdExtdDirectoryRestartInfo = 20, // 0x14
72+
MaximumFileInfoByHandlesClass
73+
} FILE_INFO_BY_HANDLE_CLASS, *PFILE_INFO_BY_HANDLE_CLASS;
74+
#endif //(WINVER < 0x0600)
75+
3976
#if !defined(IMAGE_SIZEOF_NT_OPTIONAL_HEADER)
4077
#include <winnt.h>
4178
#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER sizeof(IMAGE_OPTIONAL_HEADER)
@@ -833,6 +870,99 @@ getVersionInfo(int verbose)
833870
*/
834871
#define exit(ec) do { _cexit(ec); ExitProcess(ec); } while (0)
835872

873+
/*
874+
* Allow to test if the standard input/output files are from a console or not
875+
* Inspired of: https://fossies.org/linux/misc/vim-8.0.tar.bz2/vim80/src/iscygpty.c?m=t
876+
* Return values:
877+
* -1 - Error
878+
* 0 - no console (windows only)
879+
* 1 - normal terminal (unix terminal / windows console)
880+
* 2 - pipe
881+
* 3 - file
882+
* 4 - cygwin terminal (windows only)
883+
*/
884+
sqInt fileHandleType(HANDLE fdHandle) {
885+
if (fdHandle == INVALID_HANDLE_VALUE) {
886+
return -1;
887+
}
888+
889+
/* In case of Windows Shell case */
890+
DWORD fileType = GetFileType(fdHandle);
891+
if (fileType == FILE_TYPE_CHAR)
892+
/* The specified file is a character file, typically an LPT device or a console. */
893+
/* https://msdn.microsoft.com/en-us/library/windows/desktop/aa364960(v=vs.85).aspx */
894+
return 1;
895+
896+
/* In case of Unix emulator, we need to parse the name of the pipe */
897+
898+
/* Cygwin/msys's pty is a pipe. */
899+
if (fileType != FILE_TYPE_PIPE) {
900+
if (fileType == FILE_TYPE_DISK)
901+
return 3; //We have a file here
902+
if (fileType == FILE_TYPE_UNKNOWN && GetLastError() == ERROR_INVALID_HANDLE)
903+
return 0; //No stdio allocated
904+
return -1;
905+
}
906+
907+
int size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH;
908+
FILE_NAME_INFO *nameinfo;
909+
WCHAR *p = NULL;
910+
911+
typedef BOOL(WINAPI *pfnGetFileInformationByHandleEx)(
912+
HANDLE hFile,
913+
FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
914+
LPVOID lpFileInformation,
915+
DWORD dwBufferSize
916+
);
917+
static pfnGetFileInformationByHandleEx pGetFileInformationByHandleEx = NULL;
918+
if (!pGetFileInformationByHandleEx) {
919+
pGetFileInformationByHandleEx = (pfnGetFileInformationByHandleEx)
920+
GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetFileInformationByHandleEx");
921+
if (!pGetFileInformationByHandleEx)
922+
return -1;
923+
}
924+
925+
nameinfo = malloc(size);
926+
if (nameinfo == NULL) {
927+
return -1;
928+
}
929+
/* Check the name of the pipe: '\{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master' */
930+
if (pGetFileInformationByHandleEx(fdHandle, FileNameInfo, nameinfo, size)) {
931+
nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = L'\0';
932+
p = nameinfo->FileName;
933+
//Check that the pipe name contains msys or cygwin
934+
if ((((wcsstr(p, L"msys-") || wcsstr(p, L"cygwin-"))) &&
935+
(wcsstr(p, L"-pty") && wcsstr(p, L"-master")))) {
936+
//The openned pipe is a msys xor cygwin pipe to pty
937+
free(nameinfo);
938+
return 4;
939+
}
940+
else
941+
free(nameinfo);
942+
return 2; //else it is just a standard pipe
943+
}
944+
free(nameinfo);
945+
return -1;
946+
}
947+
948+
/*
949+
* Allow to test whether the file handle is from a console or not
950+
* 1 if one of the stdio is redirected to a console pipe, else 0 (and in this case, a file should be created)
951+
*/
952+
sqInt isFileHandleATTY(HANDLE fdHandle) {
953+
sqInt res = fileHandleType(fdHandle) ;
954+
return res == 1 || res == 4;
955+
}
956+
957+
/*
958+
* Allow to test whether one of the standard input/output files is from a console or not
959+
* 1 if one of the stdio is redirected to a console pipe, else 0 (and in this case, a file should be created)
960+
*/
961+
sqInt isOneStdioDescriptorATTY() {
962+
return isFileHandleATTY(GetStdHandle(STD_INPUT_HANDLE)) ||
963+
isFileHandleATTY(GetStdHandle(STD_OUTPUT_HANDLE)) || isFileHandleATTY(GetStdHandle(STD_ERROR_HANDLE));
964+
}
965+
836966
static void
837967
versionInfo(void)
838968
{
@@ -1590,23 +1720,7 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
15901720
* allocation failures unless running as a console app because doing so
15911721
* via a MessageBox will make the system unusable.
15921722
*/
1593-
#if 0 /* This way used to work. Does no longer. */
1594-
DWORD mode;
1595-
1596-
fIsConsole = GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &mode);
1597-
#elif 0 /* This does /not/ work with STD_INPUT_HANDLE or STD_OUTPUT_HANDLE */
1598-
CONSOLE_SCREEN_BUFFER_INFO csbi;
1599-
1600-
if ((fIsConsole = GetConsoleScreenBufferInfo
1601-
(GetStdHandle(STD_INPUT_HANDLE), &csbi)))
1602-
fIsConsole = csbi.dwCursorPosition.X || csbi.dwCursorPosition.Y;
1603-
#else /* This /does/ work; see */
1604-
/* https://stackoverflow.com/questions/9009333/how-to-check-if-the-program-is-run-from-a-console */
1605-
HWND consoleWnd = GetConsoleWindow();
1606-
DWORD dwProcessId;
1607-
GetWindowThreadProcessId(consoleWnd, &dwProcessId);
1608-
fIsConsole = GetCurrentProcessId() != dwProcessId;
1609-
#endif
1723+
fIsConsole = isOneStdioDescriptorATTY();
16101724

16111725
/* a few things which need to be done first */
16121726
gatherSystemInfo();

0 commit comments

Comments
 (0)