/* * Copyright (c) 1996 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Network Research * Group at Lawrence Berkeley National Laboratory. * 4. Neither the name of the University nor of the Laboratory may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This module contributed by John Brezak . * January 31, 1996 */ #ifndef lint static char rcsid[] = "@(#) $Header: /cvsroot/nsnam/nam-1/win32.c,v 1.2 1997/11/22 22:59:36 tecklee Exp $ (LBL)"; #endif #include #include #include #include #include #include #include #include #include #include #include #include /* forward declarations */ int WinGetUserName(ClientData, Tcl_Interp*, int ac, char**av); int WinGetHostName(ClientData, Tcl_Interp*, int ac, char**av); int WinPutRegistry(ClientData, Tcl_Interp*, int ac, char**av); int WinGetRegistry(ClientData, Tcl_Interp*, int ac, char**av); int WinExit(ClientData, Tcl_Interp*, int ac, char**av); void TkConsoleCreate(); int TkConsoleInit(Tcl_Interp* interp); #ifdef STATIC_TCLTK extern HINSTANCE Tk_GetHINSTANCE(); extern BOOL APIENTRY Tk_LibMain(HINSTANCE hInstance,DWORD reason,LPVOID reserved); extern BOOL APIENTRY Tcl_LibMain(HINSTANCE hInstance,DWORD reason,LPVOID reserved); void static_exit(void){ HINSTANCE hInstance = Tk_GetHINSTANCE(); Tcl_LibMain(hInstance, DLL_PROCESS_DETACH, NULL); Tk_LibMain(hInstance, DLL_PROCESS_DETACH, NULL); } #endif int strcasecmp(const char *s1, const char *s2) { return stricmp(s1, s2); } extern void TkWinXInit(HINSTANCE hInstance); extern int main(int argc, const char *argv[]); #ifndef STATIC_TCLTK extern int __argc; extern char **__argv; #endif static char argv0[255]; /* Buffer used to hold argv0. */ char *__progname = "nam"; void ShowMessage(int level, char *msg) { MessageBeep(level); MessageBox(NULL, msg, __progname, level | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND); } int SetupConsole() { // stuff from knowledge base Q105305 (see that for details) // open a console and do the work around to get the console to work in all // cases int hCrt; FILE *hf=0; const COORD screenSz = {80, 2000}; /* size of console buffer */ AllocConsole(); hf=0; hCrt = _open_osfhandle( (long)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT); if (hCrt!=-1) hf = _fdopen(hCrt, "w"); if (hf!=0) *stdout = *hf; if (hCrt==-1 || hf==0 || 0!=setvbuf(stdout, NULL, _IONBF, 0)) { ShowMessage(MB_ICONINFORMATION, "unable to reroute stdout"); return FALSE; } SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), screenSz); hf=0; hCrt = _open_osfhandle( (long)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT); if (hCrt!=-1) hf = _fdopen(hCrt, "w"); if (hf!=0) *stderr = *hf; if (hCrt==-1 || hf==0 || 0!=setvbuf(stderr, NULL, _IONBF, 0)) { ShowMessage(MB_ICONINFORMATION, "reroute stderr failed in SetupConsole"); return FALSE; } hf=0; hCrt = _open_osfhandle((long)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT); if (hCrt!=-1) hf = _fdopen(hCrt, "r"); if (hf!=0) *stdin = *hf; if (hCrt==-1 || hf==0 || 0!=setvbuf(stdin, NULL, _IONBF, 0)) { ShowMessage(MB_ICONINFORMATION, "reroute stdin failed in SetupConsole"); return FALSE; } return TRUE; } int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { char *p; WSADATA WSAdata; int retcode; #ifdef STATIC_TCLTK Tcl_LibMain(hInstance, DLL_PROCESS_ATTACH, NULL); Tk_LibMain(hInstance, DLL_PROCESS_ATTACH, NULL); atexit(static_exit); #endif setlocale(LC_ALL, "C"); /* XXX * initialize our socket interface plus the tcl 7.5 socket * interface (since they redefine some routines we call). * eventually we should just call the tcl sockets but at * the moment that's hard to set up since they only support * tcp in the notifier. */ if (WSAStartup(MAKEWORD (1, 1), &WSAdata)) { perror("Windows Sockets init failed"); abort(); } /* TclHasSockets(NULL); TkWinXInit(hInstance); */ /* * Increase the application queue size from default value of 8. * At the default value, cross application SendMessage of WM_KILLFOCUS * will fail because the handler will not be able to do a PostMessage! * This is only needed for Windows 3.x, since NT dynamically expands * the queue. */ SetMessageQueue(64); GetModuleFileName(NULL, argv0, 255); p = argv0; __progname = strrchr(p, '/'); if (__progname != NULL) { __progname++; } else { __progname = strrchr(p, '\\'); if (__progname != NULL) { __progname++; } else { __progname = p; } } if (__argc>1) { SetupConsole(); } retcode=main(__argc, (const char**)__argv); if (retcode!=0) { assert(FALSE); // don't die without letting user know why } return retcode; } static char szTemp[4096]; int outputErr(const char* szPrefix, Tcl_Interp* interp) { char *szError=Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); int l = strlen(szPrefix) + strlen(interp->result) + 2 + strlen(szError) + 1; char* szMsg = szTemp; if (l>4096) szMsg = (char*)malloc(l*sizeof(char)); strcpy(szMsg, szPrefix); // strcat(szMsg, interp->result); strcat(szMsg, "\n"); strcat(szMsg, szError); OutputDebugString(szMsg); ShowMessage(MB_ICONERROR, szMsg); assert(FALSE); if (szMsg != szTemp) free(szMsg); return 0; } void win_perror(const char *msg) { DWORD cMsgLen; CHAR *msgBuf; DWORD dwError = GetLastError(); cMsgLen = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | 40, NULL, dwError, MAKELANGID(0, SUBLANG_ENGLISH_US), (LPTSTR) &msgBuf, 512, NULL); if (!cMsgLen) fprintf(stderr, "%s%sError code %lu\n", msg?msg:"", msg?": ":"", dwError); else { fprintf(stderr, "%s%s%s\n", msg?msg:"", msg?": ":"", msgBuf); LocalFree((HLOCAL)msgBuf); } } #if 0 static char szTemp[4096]; int printf(const char *fmt, ...) { int retval; va_list ap; va_start (ap, fmt); retval = vsprintf(szTemp, fmt, ap); OutputDebugString(szTemp); ShowMessage(MB_ICONINFORMATION, szTemp); va_end (ap); return(retval); } int fprintf(FILE *f, const char *fmt, ...) { int retval; va_list ap; va_start (ap, fmt); if (f == stderr) { retval = vsprintf(szTemp, fmt, ap); OutputDebugString(szTemp); ShowMessage(MB_ICONERROR, szTemp); va_end (ap); } else retval = vfprintf(f, fmt, ap); return(retval); } void perror(const char *msg) { DWORD cMsgLen; CHAR *msgBuf; DWORD dwError = GetLastError(); cMsgLen = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | 40, NULL, dwError, MAKELANGID(0, SUBLANG_ENGLISH_US), (LPTSTR) &msgBuf, 512, NULL); if (!cMsgLen) fprintf(stderr, "%s%sError code %lu\n", msg?msg:"", msg?": ":"", dwError); else { fprintf(stderr, "%s%s%s\n", msg?msg:"", msg?": ":"", msgBuf); LocalFree((HLOCAL)msgBuf); } } int WinPutsCmd(clientData, interp, argc, argv) ClientData clientData; /* ConsoleInfo pointer. */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ { int i, newline; char *fileId; i = 1; newline = 1; if ((argc >= 2) && (strcmp(argv[1], "-nonewline") == 0)) { newline = 0; i++; } if ((i < (argc-3)) || (i >= argc)) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ?-nonewline? ?fileId? string\"", (char *) NULL); return TCL_ERROR; } /* * The code below provides backwards compatibility with an old * form of the command that is no longer recommended or documented. */ if (i == (argc-3)) { if (strncmp(argv[i+2], "nonewline", strlen(argv[i+2])) != 0) { Tcl_AppendResult(interp, "bad argument \"", argv[i+2], "\": should be \"nonewline\"", (char *) NULL); return TCL_ERROR; } newline = 0; } if (i == (argc-1)) { fileId = "stdout"; } else { fileId = argv[i]; i++; } if (strcmp(fileId, "stdout") == 0 || strcmp(fileId, "stderr") == 0) { char *result; int level; if (newline) { int len = strlen(argv[i]); result = ckalloc(len+2); memcpy(result, argv[i], len); result[len] = '\n'; result[len+1] = 0; } else { result = argv[i]; } if (strcmp(fileId, "stdout") == 0) { level = MB_ICONINFORMATION; } else { level = MB_ICONERROR; } OutputDebugString(result); ShowMessage(level, result); if (newline) ckfree(result); return TCL_OK; } else { extern int Tcl_PutsCmd(ClientData clientData, Tcl_Interp *interp, int argc, char **argv); return (Tcl_PutsCmd(clientData, interp, argc, argv)); } } #endif int WinGetUserName(clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char *argv[]; /* Argument strings. */ { char user[256]; int size = sizeof(user); if (!GetUserName(user, &size)) { Tcl_AppendResult(interp, "GetUserName failed", NULL); return TCL_ERROR; } Tcl_AppendResult(interp, user, NULL); return TCL_OK; } int WinGetHostName(clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char *argv[]; /* Argument strings. */ { char hostname[MAXGETHOSTSTRUCT]; if (SOCKET_ERROR == gethostname(hostname, MAXGETHOSTSTRUCT)) { Tcl_AddErrorInfo(interp, "gethostname failed!"); } Tcl_AppendResult(interp, hostname, NULL); return TCL_OK; } static HKEY regroot(root) char *root; { if (strcasecmp(root, "HKEY_LOCAL_MACHINE") == 0) return HKEY_LOCAL_MACHINE; else if (strcasecmp(root, "HKEY_CURRENT_USER") == 0) return HKEY_CURRENT_USER; else if (strcasecmp(root, "HKEY_USERS") == 0) return HKEY_USERS; else if (strcasecmp(root, "HKEY_CLASSES_ROOT") == 0) return HKEY_CLASSES_ROOT; else return NULL; } int WinGetRegistry(clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ { HKEY hKey, hRootKey; DWORD dwType; DWORD len, retCode; CHAR *regRoot, *regPath, *keyValue, *keyData; int retval = TCL_ERROR; if (argc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], "key value\"", (char *) NULL); return TCL_ERROR; } regRoot = argv[1]; keyValue = argv[2]; regPath = strchr(regRoot, '\\'); *regPath++ = '\0'; if ((hRootKey = regroot(regRoot)) == NULL) { Tcl_AppendResult(interp, "Unknown registry root \"", regRoot, "\"", NULL); return (TCL_ERROR); } retCode = RegOpenKeyEx(hRootKey, regPath, 0, KEY_READ, &hKey); if (retCode == ERROR_SUCCESS) { retCode = RegQueryValueEx(hKey, keyValue, NULL, &dwType, NULL, &len); if (retCode == ERROR_SUCCESS && dwType == REG_SZ && len) { keyData = (CHAR *) ckalloc(len); retCode = RegQueryValueEx(hKey, keyValue, NULL, NULL, keyData, &len); if (retCode == ERROR_SUCCESS) { Tcl_AppendResult(interp, keyData, NULL); free(keyData); retval = TCL_OK; } } RegCloseKey(hKey); } if (retval == TCL_ERROR) { Tcl_AppendResult(interp, "Cannot find registry entry \"", regRoot, "\\", regPath, "\\", keyValue, "\"", NULL); } return (retval); } int WinPutRegistry(clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ { HKEY hKey, hRootKey; DWORD retCode; CHAR *regRoot, *regPath, *keyValue, *keyData; DWORD new; int result = TCL_OK; if (argc != 4) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], "key value data\"", (char *) NULL); return TCL_ERROR; } regRoot = argv[1]; keyValue = argv[2]; keyData = argv[3]; regPath = strchr(regRoot, '\\'); *regPath++ = '\0'; if ((hRootKey = regroot(regRoot)) == NULL) { Tcl_AppendResult(interp, "Unknown registry root \"", regRoot, "\"", NULL); return (TCL_ERROR); } retCode = RegCreateKeyEx(hRootKey, regPath, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &new); if (retCode == ERROR_SUCCESS) { retCode = RegSetValueEx(hKey, keyValue, 0, REG_SZ, keyData, strlen(keyData)); if (retCode != ERROR_SUCCESS) { Tcl_AppendResult(interp, "unable to set key \"", regRoot, "\\", regPath, "\" with value \"", keyValue, "\"", (char *) NULL); result = TCL_ERROR; } RegCloseKey(hKey); } else { Tcl_AppendResult(interp, "unable to create key \"", regRoot, "\\", regPath, "\"", (char *) NULL); result = TCL_ERROR; } return (result); } /* does everything the normal exit command does, but shows a dialog box if * there is an error so that the console does not vaporize */ int WinExit(dummy, interp, argc, argv) ClientData dummy; /* Not used. */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; { int value; char buffer[100]; if ((argc != 1) && (argc != 2)) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ?returnCode?\"", (char *) NULL); return TCL_ERROR; } if (argc == 1) { value = 0; } else if (Tcl_GetInt(interp, argv[1], &value) != TCL_OK) { return TCL_ERROR; } /* all the error information should be in the console, so just * show the message and wait for user input */ if (value != 0) { ShowMessage(MB_ICONERROR, "Application exiting with error!"); } /* call the usual (renamed) tcl exit command */ sprintf(buffer, "tcl_exit %d", value); Tcl_Eval(interp, buffer); /*NOTREACHED*/ return TCL_OK; } static char initScript[]= "proc init {} {\n\ global tcl_library tcl_platform tcl_version tcl_patchLevel env errorInfo\n\ global tcl_pkgPath\n\ rename init {}\n\ set errors {}\n\ proc tcl_envTraceProc {lo n1 n2 op} {\n\ global env\n\ set x $env($n2)\n\ set env($lo) $x\n\ set env([string toupper $lo]) $x\n\ }\n\ foreach p [array names env] {\n\ set u [string toupper $p]\n\ if {$u != $p} {\n\ switch -- $u {\n\ COMSPEC -\n\ PATH {\n\ if {![info exists env($u)]} {\n\ set env($u) $env($p)\n\ }\n\ trace variable env($p) w [list tcl_envTraceProc $p]\n\ trace variable env($u) w [list tcl_envTraceProc $p]\n\ }\n\ }\n\ }\n\ }\n\ if {![info exists env(COMSPEC)]} {\n\ if {$tcl_platform(os) == {Windows NT}} {\n\ set env(COMSPEC) cmd.exe\n\ } else {\n\ set env(COMSPEC) command.com\n\ }\n\ } \n\ }\n\ init\n"; int platformInit(Tcl_Interp* interp) { /* tcl.CreateCommand("puts", WinPutsCmd, (ClientData)0); */ Tcl_CreateCommand(interp, "getusername", WinGetUserName, (ClientData)0, (Tcl_CmdDeleteProc*)0); Tcl_CreateCommand(interp, "gethostname", WinGetHostName, (ClientData)0, (Tcl_CmdDeleteProc*)0); Tcl_CreateCommand(interp, "putregistry", WinPutRegistry, (ClientData)0, (Tcl_CmdDeleteProc*)0); Tcl_CreateCommand(interp, "getregistry", WinGetRegistry, (ClientData)0, (Tcl_CmdDeleteProc*)0); Tcl_Eval(interp, initScript); if (TCL_OK==Tcl_Eval(interp, "rename exit tcl_exit")) { Tcl_CreateCommand(interp, "exit", WinExit, (ClientData)0, (Tcl_CmdDeleteProc*)0); } else { fprintf(stderr, "rename of exit proc failed!"); } #if 0 /* * Initialize the console only if we are running as an interactive * application. */ { char* interactive; if ((interactive=Tcl_GetVar(interp, "tcl_interactive", TCL_GLOBAL_ONLY)) && !strcmp(interactive, "1")) { /* * Create the console channels and install them as the standard * channels. All I/O will be discarded until TkConsoleInit is * called to attach the console to a text widget. */ TkConsoleCreate(); if (TkConsoleInit(interp) == TCL_ERROR) { fprintf(stderr, "error calling TkConsoleInit\n"); } } } #endif return TCL_OK; }